1 //
2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.util.resource;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.net.MalformedURLException;
25 import java.net.URL;
26 import java.net.URLConnection;
27 import java.nio.channels.ReadableByteChannel;
28 import java.security.Permission;
29
30 import org.eclipse.jetty.util.URIUtil;
31 import org.eclipse.jetty.util.log.Log;
32 import org.eclipse.jetty.util.log.Logger;
33
34 /* ------------------------------------------------------------ */
35 /** Abstract resource class.
36 */
37 public class URLResource extends Resource
38 {
39 private static final Logger LOG = Log.getLogger(URLResource.class);
40 protected final URL _url;
41 protected final String _urlString;
42
43 protected URLConnection _connection;
44 protected InputStream _in=null;
45 transient boolean _useCaches = Resource.__defaultUseCaches;
46
47 /* ------------------------------------------------------------ */
48 protected URLResource(URL url, URLConnection connection)
49 {
50 _url = url;
51 _urlString=_url.toExternalForm();
52 _connection=connection;
53 }
54
55 /* ------------------------------------------------------------ */
56 protected URLResource (URL url, URLConnection connection, boolean useCaches)
57 {
58 this (url, connection);
59 _useCaches = useCaches;
60 }
61
62 /* ------------------------------------------------------------ */
63 protected synchronized boolean checkConnection()
64 {
65 if (_connection==null)
66 {
67 try{
68 _connection=_url.openConnection();
69 _connection.setUseCaches(_useCaches);
70 }
71 catch(IOException e)
72 {
73 LOG.ignore(e);
74 }
75 }
76 return _connection!=null;
77 }
78
79 /* ------------------------------------------------------------ */
80 /** Release any resources held by the resource.
81 */
82 @Override
83 public synchronized void close()
84 {
85 if (_in!=null)
86 {
87 try{_in.close();}catch(IOException e){LOG.ignore(e);}
88 _in=null;
89 }
90
91 if (_connection!=null)
92 _connection=null;
93 }
94
95 /* ------------------------------------------------------------ */
96 /**
97 * Returns true if the represented resource exists.
98 */
99 @Override
100 public boolean exists()
101 {
102 try
103 {
104 synchronized(this)
105 {
106 if (checkConnection() && _in==null )
107 _in = _connection.getInputStream();
108 }
109 }
110 catch (IOException e)
111 {
112 LOG.ignore(e);
113 }
114 return _in!=null;
115 }
116
117 /* ------------------------------------------------------------ */
118 /**
119 * Returns true if the represented resource is a container/directory.
120 * If the resource is not a file, resources ending with "/" are
121 * considered directories.
122 */
123 @Override
124 public boolean isDirectory()
125 {
126 return exists() && _urlString.endsWith("/");
127 }
128
129
130 /* ------------------------------------------------------------ */
131 /**
132 * Returns the last modified time
133 */
134 @Override
135 public long lastModified()
136 {
137 if (checkConnection())
138 return _connection.getLastModified();
139 return -1;
140 }
141
142
143 /* ------------------------------------------------------------ */
144 /**
145 * Return the length of the resource
146 */
147 @Override
148 public long length()
149 {
150 if (checkConnection())
151 return _connection.getContentLength();
152 return -1;
153 }
154
155 /* ------------------------------------------------------------ */
156 /**
157 * Returns an URL representing the given resource
158 */
159 @Override
160 public URL getURL()
161 {
162 return _url;
163 }
164
165 /* ------------------------------------------------------------ */
166 /**
167 * Returns an File representing the given resource or NULL if this
168 * is not possible.
169 */
170 @Override
171 public File getFile()
172 throws IOException
173 {
174 // Try the permission hack
175 if (checkConnection())
176 {
177 Permission perm = _connection.getPermission();
178 if (perm instanceof java.io.FilePermission)
179 return new File(perm.getName());
180 }
181
182 // Try the URL file arg
183 try {return new File(_url.getFile());}
184 catch(Exception e) {LOG.ignore(e);}
185
186 // Don't know the file
187 return null;
188 }
189
190 /* ------------------------------------------------------------ */
191 /**
192 * Returns the name of the resource
193 */
194 @Override
195 public String getName()
196 {
197 return _url.toExternalForm();
198 }
199
200
201 /* ------------------------------------------------------------ */
202 /**
203 * Returns an input stream to the resource. The underlying
204 * url connection will be nulled out to prevent re-use.
205 */
206 @Override
207 public synchronized InputStream getInputStream()
208 throws java.io.IOException
209 {
210 return getInputStream (true); //backwards compatibility
211 }
212
213
214
215 /* ------------------------------------------------------------ */
216 /**
217 * Returns an input stream to the resource, optionally nulling
218 * out the underlying url connection. If the connection is not
219 * nulled out, a subsequent call to getInputStream() may return
220 * an existing and already in-use input stream - this depends on
221 * the url protocol. Eg JarURLConnection does not reuse inputstreams.
222 *
223 * @param resetConnection if true the connection field is set to null
224 * @return the inputstream for this resource
225 * @throws IOException if unable to open the input stream
226 */
227 protected synchronized InputStream getInputStream(boolean resetConnection)
228 throws IOException
229 {
230 if (!checkConnection())
231 throw new IOException( "Invalid resource");
232
233 try
234 {
235 if( _in != null)
236 {
237 InputStream in = _in;
238 _in=null;
239 return in;
240 }
241 return _connection.getInputStream();
242 }
243 finally
244 {
245 if (resetConnection)
246 {
247 _connection=null;
248 if (LOG.isDebugEnabled()) LOG.debug("Connection nulled");
249 }
250 }
251 }
252
253 /* ------------------------------------------------------------ */
254 @Override
255 public ReadableByteChannel getReadableByteChannel() throws IOException
256 {
257 return null;
258 }
259
260 /* ------------------------------------------------------------ */
261 /**
262 * Deletes the given resource
263 */
264 @Override
265 public boolean delete()
266 throws SecurityException
267 {
268 throw new SecurityException( "Delete not supported");
269 }
270
271 /* ------------------------------------------------------------ */
272 /**
273 * Rename the given resource
274 */
275 @Override
276 public boolean renameTo( Resource dest)
277 throws SecurityException
278 {
279 throw new SecurityException( "RenameTo not supported");
280 }
281
282 /* ------------------------------------------------------------ */
283 /**
284 * Returns a list of resource names contained in the given resource
285 */
286 @Override
287 public String[] list()
288 {
289 return null;
290 }
291
292 /* ------------------------------------------------------------ */
293 /**
294 * Returns the resource contained inside the current resource with the
295 * given name
296 */
297 @Override
298 public Resource addPath(String path)
299 throws IOException,MalformedURLException
300 {
301 if (path==null)
302 return null;
303
304 path = URIUtil.canonicalPath(path);
305
306 return newResource(URIUtil.addPaths(_url.toExternalForm(),URIUtil.encodePath(path)), _useCaches);
307 }
308
309 /* ------------------------------------------------------------ */
310 @Override
311 public String toString()
312 {
313 return _urlString;
314 }
315
316 /* ------------------------------------------------------------ */
317 @Override
318 public int hashCode()
319 {
320 return _urlString.hashCode();
321 }
322
323 /* ------------------------------------------------------------ */
324 @Override
325 public boolean equals( Object o)
326 {
327 return o instanceof URLResource && _urlString.equals(((URLResource)o)._urlString);
328 }
329
330 /* ------------------------------------------------------------ */
331 public boolean getUseCaches ()
332 {
333 return _useCaches;
334 }
335
336 /* ------------------------------------------------------------ */
337 @Override
338 public boolean isContainedIn (Resource containingResource) throws MalformedURLException
339 {
340 return false;
341 }
342 }