1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 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
19package org.eclipse.jetty.util.resource;
20
21import java.io.File;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.OutputStream;
25import java.net.MalformedURLException;
26import java.net.URL;
27import java.net.URLConnection;
28import java.security.Permission;
29
30import org.eclipse.jetty.util.URIUtil;
31import org.eclipse.jetty.util.log.Log;
32import org.eclipse.jetty.util.log.Logger;
33
34/* ------------------------------------------------------------ */
35/** Abstract resource class.
36 */
37public class URLResource extends Resource
38{
39    private static final Logger LOG = Log.getLogger(URLResource.class);
40    protected URL _url;
41    protected 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.toString();
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 release()
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 respresenetd 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() && _url.toString().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     * Returns an input stream to the resource
203     */
204    @Override
205    public synchronized InputStream getInputStream()
206        throws java.io.IOException
207    {
208        if (!checkConnection())
209            throw new IOException( "Invalid resource");
210
211        try
212        {
213            if( _in != null)
214            {
215                InputStream in = _in;
216                _in=null;
217                return in;
218            }
219            return _connection.getInputStream();
220        }
221        finally
222        {
223            _connection=null;
224        }
225    }
226
227
228    /* ------------------------------------------------------------ */
229    /**
230     * Returns an output stream to the resource
231     */
232    @Override
233    public OutputStream getOutputStream()
234        throws java.io.IOException, SecurityException
235    {
236        throw new IOException( "Output not supported");
237    }
238
239    /* ------------------------------------------------------------ */
240    /**
241     * Deletes the given resource
242     */
243    @Override
244    public boolean delete()
245        throws SecurityException
246    {
247        throw new SecurityException( "Delete not supported");
248    }
249
250    /* ------------------------------------------------------------ */
251    /**
252     * Rename the given resource
253     */
254    @Override
255    public boolean renameTo( Resource dest)
256        throws SecurityException
257    {
258        throw new SecurityException( "RenameTo not supported");
259    }
260
261    /* ------------------------------------------------------------ */
262    /**
263     * Returns a list of resource names contained in the given resource
264     */
265    @Override
266    public String[] list()
267    {
268        return null;
269    }
270
271    /* ------------------------------------------------------------ */
272    /**
273     * Returns the resource contained inside the current resource with the
274     * given name
275     */
276    @Override
277    public Resource addPath(String path)
278        throws IOException,MalformedURLException
279    {
280        if (path==null)
281            return null;
282
283        path = URIUtil.canonicalPath(path);
284
285        return newResource(URIUtil.addPaths(_url.toExternalForm(),path));
286    }
287
288    /* ------------------------------------------------------------ */
289    @Override
290    public String toString()
291    {
292        return _urlString;
293    }
294
295    /* ------------------------------------------------------------ */
296    @Override
297    public int hashCode()
298    {
299        return _urlString.hashCode();
300    }
301
302    /* ------------------------------------------------------------ */
303    @Override
304    public boolean equals( Object o)
305    {
306        return o instanceof URLResource && _urlString.equals(((URLResource)o)._urlString);
307    }
308
309    /* ------------------------------------------------------------ */
310    public boolean getUseCaches ()
311    {
312        return _useCaches;
313    }
314
315    /* ------------------------------------------------------------ */
316    @Override
317    public boolean isContainedIn (Resource containingResource) throws MalformedURLException
318    {
319        return false; //TODO check this!
320    }
321}
322