001//========================================================================
002//$Id: JettyWebAppContext.java 6624 2011-05-27 04:51:32Z gregw $
003//Copyright 2006 Mort Bay Consulting Pty. Ltd.
004//------------------------------------------------------------------------
005//Licensed under the Apache License, Version 2.0 (the "License");
006//you may not use this file except in compliance with the License.
007//You may obtain a copy of the License at
008//http://www.apache.org/licenses/LICENSE-2.0
009//Unless required by applicable law or agreed to in writing, software
010//distributed under the License is distributed on an "AS IS" BASIS,
011//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012//See the License for the specific language governing permissions and
013//limitations under the License.
014//========================================================================
015
016package org.jszip.jetty;
017
018import java.io.File;
019import java.io.IOException;
020import java.net.MalformedURLException;
021import java.net.URL;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.TreeSet;
028
029import org.apache.commons.lang3.StringUtils;
030import org.eclipse.jetty.plus.webapp.EnvConfiguration;
031import org.eclipse.jetty.util.URIUtil;
032import org.eclipse.jetty.util.log.Log;
033import org.eclipse.jetty.util.resource.Resource;
034import org.eclipse.jetty.util.resource.ResourceCollection;
035import org.eclipse.jetty.webapp.Configuration;
036import org.eclipse.jetty.webapp.FragmentConfiguration;
037import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
038import org.eclipse.jetty.webapp.MetaInfConfiguration;
039import org.eclipse.jetty.webapp.WebAppContext;
040import org.eclipse.jetty.webapp.WebInfConfiguration;
041import org.eclipse.jetty.webapp.WebXmlConfiguration;
042
043/**
044 * JettyWebAppContext
045 *
046 * Extends the WebAppContext to specialize for the maven environment.
047 * We pass in the list of files that should form the classpath for
048 * the webapp when executing in the plugin, and any jetty-env.xml file
049 * that may have been configured.
050 *
051 */
052public class JettyWebAppContext extends WebAppContext
053{
054    private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes";
055    private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib";
056
057    private final List<File> webInfClasses = new ArrayList<File>();
058    private final List<File> webInfJars = new ArrayList<File>();
059    private final Map<String, File> webInfJarMap = new HashMap<String, File>();
060    private final EnvConfiguration envConfig;
061    private List<File> classpathFiles;
062    private String jettyEnvXml;
063    private List<Resource> overlays;
064    private boolean unpackOverlays;
065    private String containerIncludeJarPattern = ".*/servlet-api-[^/]*\\.jar$";
066
067    public JettyWebAppContext ()
068    throws Exception
069    {
070        super();
071        setConfigurations(new Configuration[]{
072                new WebXmlConfiguration(),
073                new MetaInfConfiguration(),
074                new FragmentConfiguration(),
075                envConfig = new EnvConfiguration(),
076                new org.eclipse.jetty.plus.webapp.PlusConfiguration(),
077                new JettyWebXmlConfiguration(),
078        });
079    }
080    public void setContainerIncludeJarPattern(String pattern)
081    {
082        containerIncludeJarPattern = pattern;
083    }
084
085    public String getContainerIncludeJarPattern()
086    {
087        return containerIncludeJarPattern;
088    }
089
090    public boolean getUnpackOverlays()
091    {
092        return unpackOverlays;
093    }
094
095    public void setUnpackOverlays(boolean unpackOverlays)
096    {
097        this.unpackOverlays = unpackOverlays;
098    }
099
100    public void setClassPathFiles(List<File> classpathFiles)
101    {
102        this.classpathFiles = classpathFiles;
103    }
104
105    public List<File> getClassPathFiles()
106    {
107        return this.classpathFiles;
108    }
109
110    public void setOverlays (List<Resource> overlays)
111    {
112        this.overlays = overlays;
113    }
114
115    public List<Resource> getOverlays ()
116    {
117        return this.overlays;
118    }
119
120    public void setJettyEnvXml (String jettyEnvXml)
121    {
122        this.jettyEnvXml = jettyEnvXml;
123    }
124
125    public String getJettyEnvXml()
126    {
127        return this.jettyEnvXml;
128    }
129
130
131    public void setWebInfClasses(List<File> dirs)
132    {
133        webInfClasses.addAll(dirs);
134    }
135
136    public List<File> getWebInfClasses()
137    {
138        return webInfClasses;
139    }
140
141    public void setWebInfLib (List<File> jars)
142    {
143        webInfJars.addAll(jars);
144    }
145
146    /* ------------------------------------------------------------ */
147    /**
148     * This method is provided as a conveniance for jetty maven plugin configuration
149     * @param resourceBases Array of resources strings to set as a {@link ResourceCollection}. Each resource string may be a comma separated list of resources
150     * @see Resource
151     */
152    public void setResourceBases(String[] resourceBases)
153    {
154        List<String> resources = new ArrayList<String>();
155        for (String rl:resourceBases)
156        {
157            String[] rs = rl.split(" *, *");
158            for (String r:rs)
159                resources.add(r);
160        }
161
162        setBaseResource(new ResourceCollection(resources.toArray(new String[resources.size()])));
163    }
164
165    public List<File> getWebInfLib()
166    {
167        return webInfJars;
168    }
169
170    public void doStart () throws Exception
171    {
172        setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, containerIncludeJarPattern);
173
174
175        // Initialize map containing all jars in /WEB-INF/lib
176        webInfJarMap.clear();
177        for (File file : webInfJars)
178        {
179            // Return all jar files from class path
180            String fileName = file.getName();
181            if (fileName.endsWith(".jar"))
182                webInfJarMap.put(fileName, file);
183        }
184
185        if (this.jettyEnvXml != null)
186            envConfig.setJettyEnvXml(new File(this.jettyEnvXml).toURL());
187        setShutdown(false);
188        super.doStart();
189    }
190
191    public void doStop () throws Exception
192    {
193        setShutdown(true);
194        //just wait a little while to ensure no requests are still being processed
195        Thread.currentThread().sleep(500L);
196        super.doStop();
197    }
198
199    @Override
200    public Resource getResource(String uriInContext) throws MalformedURLException
201    {
202        Resource resource = null;
203        // Try to get regular resource
204        resource = super.getResource(uriInContext);
205
206        // If no regular resource exists check for access to /WEB-INF/lib or /WEB-INF/classes
207        if ((resource == null || !resource.exists()) && uriInContext != null && webInfClasses != null)
208        {
209            String uri = URIUtil.canonicalPath(uriInContext);
210
211            try
212            {
213                // Replace /WEB-INF/classes with real classes directory
214                if (uri.startsWith(WEB_INF_CLASSES_PREFIX))
215                {
216                    Resource res = null;
217                    int i=0;
218                    while (res == null && (i < webInfClasses.size()))
219                    {
220                        String newPath = uri.replace(WEB_INF_CLASSES_PREFIX, webInfClasses.get(i).getPath());
221                        res = Resource.newResource(newPath);
222                        if (!res.exists())
223                        {
224                            res = null;
225                            i++;
226                        }
227                    }
228                    return res;
229                }
230                // Return the real jar file for all accesses to
231                // /WEB-INF/lib/*.jar
232                else if (uri.startsWith(WEB_INF_LIB_PREFIX))
233                {
234                    String jarName = uri.replace(WEB_INF_LIB_PREFIX, "");
235                    if (jarName.startsWith("/") || jarName.startsWith("\\"))
236                        jarName = jarName.substring(1);
237                    if (jarName.length()==0)
238                        return null;
239                    File jarFile = webInfJarMap.get(jarName);
240                    if (jarFile != null)
241                        return Resource.newResource(jarFile.getPath());
242
243                    return null;
244                }
245            }
246            catch (MalformedURLException e)
247            {
248                throw e;
249            }
250            catch (IOException e)
251            {
252                Log.ignore(e);
253            }
254        }
255        return resource;
256    }
257
258    @Override
259    public Set<String> getResourcePaths(String path)
260    {
261        // Try to get regular resource paths
262        Set<String> paths = super.getResourcePaths(path);
263
264        // If no paths are returned check for virtual paths /WEB-INF/classes and /WEB-INF/lib
265        if (paths.isEmpty() && path != null)
266        {
267            path = URIUtil.canonicalPath(path);
268            if (path.startsWith(WEB_INF_LIB_PREFIX))
269            {
270                paths = new TreeSet<String>();
271                for (String fileName : webInfJarMap.keySet())
272                {
273                    // Return all jar files from class path
274                    paths.add(WEB_INF_LIB_PREFIX + "/" + fileName);
275                }
276            }
277            else if (path.startsWith(WEB_INF_CLASSES_PREFIX))
278            {
279                int i=0;
280
281                while (paths.isEmpty() && (i < webInfClasses.size()))
282                {
283                    String newPath = path.replace(WEB_INF_CLASSES_PREFIX, webInfClasses.get(i).getPath());
284                    paths = super.getResourcePaths(newPath);
285                    i++;
286                }
287            }
288        }
289        return paths;
290    }
291
292    /**
293     * {@inheritDoc}
294     */
295    @Override
296    public Resource newResource(URL url) throws IOException {
297        if (url != null && StringUtils.equals("virtual", url.getProtocol())) {
298            // serve virtual URLs if asked to
299            return getBaseResource().getResource(url.getPath());
300        }
301        return super.newResource(url);
302    }
303
304    /**
305     * {@inheritDoc}
306     */
307    @Override
308    public Resource newResource(String urlOrPath) throws IOException {
309        if (urlOrPath != null && urlOrPath.startsWith("virtual:")) {
310            // serve virtual URLs if asked to
311            return getBaseResource().getResource(urlOrPath.substring("virtual:".length()));
312        }
313        return super.newResource(urlOrPath);
314    }
315}