001    /*
002     * Copyright (C) 2012 eXo Platform SAS.
003     *
004     * This is free software; you can redistribute it and/or modify it
005     * under the terms of the GNU Lesser General Public License as
006     * published by the Free Software Foundation; either version 2.1 of
007     * the License, or (at your option) any later version.
008     *
009     * This software is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this software; if not, write to the Free
016     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018     */
019    
020    package org.crsh.vfs.spi.servlet;
021    
022    import org.crsh.vfs.spi.AbstractFSDriver;
023    
024    import javax.servlet.ServletContext;
025    import java.io.File;
026    import java.io.FileInputStream;
027    import java.io.IOException;
028    import java.io.InputStream;
029    import java.net.URL;
030    import java.util.Collections;
031    import java.util.Set;
032    import java.util.regex.Matcher;
033    import java.util.regex.Pattern;
034    
035    public class ServletContextDriver extends AbstractFSDriver<String> {
036    
037      /** A valid path. */
038      static final Pattern pathPattern = Pattern.compile("^(?=/).*?((?<=/)[^/]*)?(/?)$");
039    
040      /** . */
041      private final ServletContext ctx;
042    
043      public ServletContextDriver(ServletContext ctx) {
044        if (ctx == null) {
045          throw new NullPointerException();
046        }
047    
048        //
049        this.ctx = ctx;
050      }
051    
052      public String root() throws IOException {
053        return "/";
054      }
055    
056      public String name(String file) throws IOException {
057        return matcher(file).group(1);
058      }
059    
060      public boolean isDir(String file) throws IOException {
061        Matcher matcher = matcher(file);
062        String slash = matcher.group(2);
063        return "/".equals(slash);
064      }
065    
066      public Iterable<String> children(String parent) throws IOException {
067        @SuppressWarnings("unchecked")
068        Set<String> resourcePaths = (Set<String>)ctx.getResourcePaths(parent);
069        return resourcePaths != null ? resourcePaths : Collections.<String>emptyList();
070      }
071    
072      /**
073       * The implementation attempts to get an URL that will be valid for the file system first (when the
074       * war is usually exploded) and if it is not able, it will delegate to {@link ServletContext#getResource(String)}.
075       *
076       * @param file the file path
077       * @return the URL
078       * @throws IOException any io exception
079       */
080      public URL toURL(String file) throws IOException {
081        String realPath = ctx.getRealPath(file);
082        if (realPath != null) {
083          File realFile = new File(realPath);
084          if (realFile.exists() && realFile.isFile()) {
085            return realFile.toURI().toURL();
086          }
087        }
088        return ctx.getResource(file);
089      }
090    
091      public long getLastModified(String handle) throws IOException {
092        String realPath = ctx.getRealPath(handle);
093        if (realPath != null) {
094          File realFile = new File(realPath);
095          if (realFile.exists() && realFile.isFile()) {
096            return realFile.lastModified();
097          }
098        }
099        return ctx.getResource(handle).openConnection().getLastModified();
100      }
101    
102      public InputStream open(String handle) throws IOException {
103        String realPath = ctx.getRealPath(handle);
104        if (realPath != null) {
105          File realFile = new File(realPath);
106          if (realFile.exists() && realFile.isFile()) {
107            return new FileInputStream(realFile);
108          }
109        }
110        return ctx.getResource(handle).openConnection().getInputStream();
111      }
112    
113      private Matcher matcher(String path) {
114        Matcher m = pathPattern.matcher(path);
115        if (m.matches()) {
116          return m;
117        } else {
118          throw new IllegalArgumentException("Illegal path " + path);
119        }
120      }
121    }