]> WPIA git - gigi.git/blobdiff - lib/jetty/org/eclipse/jetty/util/resource/JarFileResource.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / util / resource / JarFileResource.java
diff --git a/lib/jetty/org/eclipse/jetty/util/resource/JarFileResource.java b/lib/jetty/org/eclipse/jetty/util/resource/JarFileResource.java
new file mode 100644 (file)
index 0000000..434aa88
--- /dev/null
@@ -0,0 +1,412 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.util.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+class JarFileResource extends JarResource
+{
+    private static final Logger LOG = Log.getLogger(JarFileResource.class);
+    private JarFile _jarFile;
+    private File _file;
+    private String[] _list;
+    private JarEntry _entry;
+    private boolean _directory;
+    private String _jarUrl;
+    private String _path;
+    private boolean _exists;
+    
+    /* -------------------------------------------------------- */
+    protected JarFileResource(URL url)
+    {
+        super(url);
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected JarFileResource(URL url, boolean useCaches)
+    {
+        super(url, useCaches);
+    }
+   
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public synchronized void close()
+    {
+        _list=null;
+        _entry=null;
+        _file=null;
+        //if the jvm is not doing url caching, then the JarFiles will not be cached either,
+        //and so they are safe to close
+        if (!getUseCaches())
+        {
+            if ( _jarFile != null )
+            {
+                try
+                {
+                    LOG.debug("Closing JarFile "+_jarFile.getName());
+                    _jarFile.close();
+                }
+                catch ( IOException ioe )
+                {
+                    LOG.ignore(ioe);
+                }
+            }
+        }
+        _jarFile=null;
+        super.close();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected synchronized boolean checkConnection()
+    {
+        try
+        {
+            super.checkConnection();
+        }
+        finally
+        {
+            if (_jarConnection==null)
+            {
+                _entry=null;
+                _file=null;
+                _jarFile=null;
+                _list=null;
+            }
+        }
+        return _jarFile!=null;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected synchronized void newConnection()
+        throws IOException
+    {
+        super.newConnection();
+        
+        _entry=null;
+        _file=null;
+        _jarFile=null;
+        _list=null;
+        
+        int sep = _urlString.indexOf("!/");
+        _jarUrl=_urlString.substring(0,sep+2);
+        _path=_urlString.substring(sep+2);
+        if (_path.length()==0)
+            _path=null;   
+        _jarFile=_jarConnection.getJarFile();
+        _file=new File(_jarFile.getName());
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the represented resource exists.
+     */
+    @Override
+    public boolean exists()
+    {
+        if (_exists)
+            return true;
+
+        if (_urlString.endsWith("!/"))
+        {
+            
+            String file_url=_urlString.substring(4,_urlString.length()-2);
+            try{return newResource(file_url).exists();}
+            catch(Exception e) {LOG.ignore(e); return false;}
+        }
+        
+        boolean check=checkConnection();
+        
+        // Is this a root URL?
+        if (_jarUrl!=null && _path==null)
+        {
+            // Then if it exists it is a directory
+            _directory=check;
+            return true;
+        }
+        else 
+        {
+            // Can we find a file for it?
+            JarFile jarFile=null;
+            if (check)
+                // Yes
+                jarFile=_jarFile;
+            else
+            {
+                // No - so lets look if the root entry exists.
+                try
+                {
+                    JarURLConnection c=(JarURLConnection)((new URL(_jarUrl)).openConnection());
+                    c.setUseCaches(getUseCaches());
+                    jarFile=c.getJarFile();
+                }
+                catch(Exception e)
+                {
+                       LOG.ignore(e);
+                }
+            }
+
+            // Do we need to look more closely?
+            if (jarFile!=null && _entry==null && !_directory)
+            {
+                // OK - we have a JarFile, lets look at the entries for our path
+                Enumeration<JarEntry> e=jarFile.entries();
+                while(e.hasMoreElements())
+                {
+                    JarEntry entry = e.nextElement();
+                    String name=entry.getName().replace('\\','/');
+                    
+                    // Do we have a match
+                    if (name.equals(_path))
+                    {
+                        _entry=entry;
+                        // Is the match a directory
+                        _directory=_path.endsWith("/");
+                        break;
+                    }
+                    else if (_path.endsWith("/"))
+                    {
+                        if (name.startsWith(_path))
+                        {
+                            _directory=true;
+                            break;
+                        }
+                    }
+                    else if (name.startsWith(_path) && name.length()>_path.length() && name.charAt(_path.length())=='/')
+                    {
+                        _directory=true;
+                        break;
+                    }
+                }
+            }
+        }    
+        
+        _exists= ( _directory || _entry!=null);
+        return _exists;
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the represented resource is a container/directory.
+     * If the resource is not a file, resources ending with "/" are
+     * considered directories.
+     */
+    @Override
+    public boolean isDirectory()
+    {
+        return _urlString.endsWith("/") || exists() && _directory;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the last modified time
+     */
+    @Override
+    public long lastModified()
+    {
+        if (checkConnection() && _file!=null)
+        {
+            if (exists() && _entry!=null)
+                return _entry.getTime();
+            return _file.lastModified();
+        }
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public synchronized String[] list()
+    {
+        if (isDirectory() && _list==null)
+        {
+            List<String> list = null;
+            try
+            {
+                list = listEntries();
+            }
+            catch (Exception e)
+            {
+                //Sun's JarURLConnection impl for jar: protocol will close a JarFile in its connect() method if
+                //useCaches == false (eg someone called URLConnection with defaultUseCaches==true).
+                //As their sun.net.www.protocol.jar package caches JarFiles and/or connections, we can wind up in 
+                //the situation where the JarFile we have remembered in our _jarFile member has actually been closed
+                //by other code.
+                //So, do one retry to drop a connection and get a fresh JarFile
+                LOG.warn("Retrying list:"+e);
+                LOG.debug(e);
+                release();
+                list = listEntries();
+            }
+
+            if (list != null)
+            {
+                _list=new String[list.size()];
+                list.toArray(_list);
+            }  
+        }
+        return _list;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    private List<String> listEntries ()
+    {
+        checkConnection();
+        
+        ArrayList<String> list = new ArrayList<String>(32);
+        JarFile jarFile=_jarFile;
+        if(jarFile==null)
+        {
+            try
+            {
+                JarURLConnection jc=(JarURLConnection)((new URL(_jarUrl)).openConnection());
+                jc.setUseCaches(getUseCaches());
+                jarFile=jc.getJarFile();
+            }
+            catch(Exception e)
+            {
+
+                e.printStackTrace();
+                 LOG.ignore(e);
+            }
+                if(jarFile==null)
+                    throw new IllegalStateException();
+        }
+        
+        Enumeration<JarEntry> e=jarFile.entries();
+        String dir=_urlString.substring(_urlString.indexOf("!/")+2);
+        while(e.hasMoreElements())
+        {
+            JarEntry entry = e.nextElement();               
+            String name=entry.getName().replace('\\','/');               
+            if(!name.startsWith(dir) || name.length()==dir.length())
+            {
+                continue;
+            }
+            String listName=name.substring(dir.length());               
+            int dash=listName.indexOf('/');
+            if (dash>=0)
+            {
+                //when listing jar:file urls, you get back one
+                //entry for the dir itself, which we ignore
+                if (dash==0 && listName.length()==1)
+                    continue;
+                //when listing jar:file urls, all files and
+                //subdirs have a leading /, which we remove
+                if (dash==0)
+                    listName=listName.substring(dash+1, listName.length());
+                else
+                    listName=listName.substring(0,dash+1);
+                
+                if (list.contains(listName))
+                    continue;
+            }
+            
+            list.add(listName);
+        }
+        
+        return list;
+    }
+    
+    
+    
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Return the length of the resource
+     */
+    @Override
+    public long length()
+    {
+        if (isDirectory())
+            return -1;
+
+        if (_entry!=null)
+            return _entry.getSize();
+        
+        return -1;
+    }
+
+    
+    /**
+     * Take a Resource that possibly might use URLConnection caching
+     * and turn it into one that doesn't.
+     * @param resource
+     * @return the non-caching resource
+     */
+    public static Resource getNonCachingResource (Resource resource)
+    {
+        if (!(resource instanceof JarFileResource))
+            return resource;
+        
+        JarFileResource oldResource = (JarFileResource)resource;
+        
+        JarFileResource newResource = new JarFileResource(oldResource.getURL(), false);
+        return newResource;
+        
+    }
+    
+    /**
+     * Check if this jar:file: resource is contained in the
+     * named resource. Eg <code>jar:file:///a/b/c/foo.jar!/x.html</code> isContainedIn <code>file:///a/b/c/foo.jar</code>
+     * @param resource
+     * @return true if resource is contained in the named resource
+     * @throws MalformedURLException
+     */
+    @Override
+    public boolean isContainedIn (Resource resource) 
+    throws MalformedURLException
+    {
+        String string = _urlString;
+        int index = string.indexOf("!/");
+        if (index > 0)
+            string = string.substring(0,index);
+        if (string.startsWith("jar:"))
+            string = string.substring(4);
+        URL url = new URL(string);
+        return url.sameFile(resource.getURL());     
+    }
+}
+
+
+
+
+
+
+
+