View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.deploy.providers;
20  
21  import java.io.File;
22  import java.io.FilenameFilter;
23  import java.util.Locale;
24  
25  import org.eclipse.jetty.deploy.App;
26  import org.eclipse.jetty.deploy.ConfigurationManager;
27  import org.eclipse.jetty.deploy.util.FileID;
28  import org.eclipse.jetty.server.Server;
29  import org.eclipse.jetty.server.handler.ContextHandler;
30  import org.eclipse.jetty.util.URIUtil;
31  import org.eclipse.jetty.util.annotation.ManagedAttribute;
32  import org.eclipse.jetty.util.annotation.ManagedObject;
33  import org.eclipse.jetty.util.resource.Resource;
34  import org.eclipse.jetty.webapp.WebAppContext;
35  import org.eclipse.jetty.xml.XmlConfiguration;
36  
37  /* ------------------------------------------------------------ */
38  /** The webapps directory scanning provider.
39   * <p>
40   * This provider scans one or more directories (typically "webapps") for contexts to
41   * deploy, which may be:<ul>
42   * <li>A standard WAR file (must end in ".war")</li>
43   * <li>A directory containing an expanded WAR file</li>
44   * <li>A directory containing static content</li>
45   * <li>An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance</li>
46   * </ul>
47   * <p>
48   * To avoid double deployments and allow flexibility of the content of the scanned directories, the provider
49   * implements some heuristics to ignore some files found in the scans: <ul>
50   * <li>Hidden files (starting with ".") are ignored</li>
51   * <li>Directories with names ending in ".d" are ignored</li>
52   * <li>If a directory and a WAR file exist ( eg foo/ and foo.war) then the directory is assumed to be
53   * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)</li>
54   * <li>If a directory and a matching XML file exist ( eg foo/ and foo.xml) then the directory is assumed to be
55   * an unpacked WAR and only the XML is deployed (which may used the directory in it's configuration)</li>
56   * <li>If a WAR file and a matching XML exist (eg foo.war and foo.xml) then the WAR is assumed to
57   * be configured by the XML and only the XML is deployed.
58   * </ul>
59   * <p>For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and
60   * properties for the webapp file as "jetty.webapp" and directory as "jetty.webapps".
61   */
62  @ManagedObject("Provider for start-up deployement of webapps based on presence in directory")
63  public class WebAppProvider extends ScanningAppProvider
64  {
65      private boolean _extractWars = false;
66      private boolean _parentLoaderPriority = false;
67      private ConfigurationManager _configurationManager;
68      private String _defaultsDescriptor;
69      private File _tempDirectory;
70      private String[] _configurationClasses;
71  
72      public class Filter implements FilenameFilter
73      {
74          @Override
75          public boolean accept(File dir, String name)
76          {
77              if (!dir.exists())
78              {
79                  return false;
80              }
81              String lowername = name.toLowerCase(Locale.ENGLISH);
82  
83              File file = new File(dir,name);
84  
85              // ignore hidden files
86              if (lowername.startsWith("."))
87                  return false;
88  
89              // Ignore some directories
90              if (file.isDirectory())
91              {
92                  // is it a nominated config directory
93                  if (lowername.endsWith(".d"))
94                      return false;
95  
96                  // is it an unpacked directory for an existing war file?
97                  if (exists(name+".war")||exists(name+".WAR"))
98                      return false;
99  
100                 // is it a directory for an existing xml file?
101                 if (exists(name+".xml")||exists(name+".XML"))
102                     return false;
103 
104                 //is it a sccs dir?
105                 if ("cvs".equals(lowername) || "cvsroot".equals(lowername))
106                     return false;
107 
108                 // OK to deploy it then
109                 return true;
110             }
111 
112             // else is it a war file
113             if (lowername.endsWith(".war"))
114             {
115                 String base=name.substring(0,name.length()-4);
116                 // ignore if it is a war for an existing xml file?
117                 if (exists(base+".xml")||exists(base+".XML"))
118                     return false;
119 
120                 // OK to deploy it then
121                 return true;
122             }
123 
124             // else is it a context XML file 
125             if (lowername.endsWith(".xml"))
126                 return true;
127 
128             return false;
129         }
130     }
131 
132     /* ------------------------------------------------------------ */
133     public WebAppProvider()
134     {
135         super();
136         setFilenameFilter(new Filter());
137         setScanInterval(0);
138     }
139 
140     /* ------------------------------------------------------------ */
141     /** Get the extractWars.
142      * @return the extractWars
143      */
144     @ManagedAttribute("extract war files")
145     public boolean isExtractWars()
146     {
147         return _extractWars;
148     }
149 
150     /* ------------------------------------------------------------ */
151     /** Set the extractWars.
152      * @param extractWars the extractWars to set
153      */
154     public void setExtractWars(boolean extractWars)
155     {
156         _extractWars = extractWars;
157     }
158 
159     /* ------------------------------------------------------------ */
160     /** Get the parentLoaderPriority.
161      * @return the parentLoaderPriority
162      */
163     @ManagedAttribute("parent classloader has priority")
164     public boolean isParentLoaderPriority()
165     {
166         return _parentLoaderPriority;
167     }
168 
169     /* ------------------------------------------------------------ */
170     /** Set the parentLoaderPriority.
171      * @param parentLoaderPriority the parentLoaderPriority to set
172      */
173     public void setParentLoaderPriority(boolean parentLoaderPriority)
174     {
175         _parentLoaderPriority = parentLoaderPriority;
176     }
177     
178     /* ------------------------------------------------------------ */
179     /** Get the defaultsDescriptor.
180      * @return the defaultsDescriptor
181      */
182     @ManagedAttribute("default descriptor for webapps")
183     public String getDefaultsDescriptor()
184     {
185         return _defaultsDescriptor;
186     }
187 
188     /* ------------------------------------------------------------ */
189     /** Set the defaultsDescriptor.
190      * @param defaultsDescriptor the defaultsDescriptor to set
191      */
192     public void setDefaultsDescriptor(String defaultsDescriptor)
193     {
194         _defaultsDescriptor = defaultsDescriptor;
195     }
196 
197     /* ------------------------------------------------------------ */
198     public ConfigurationManager getConfigurationManager()
199     {
200         return _configurationManager;
201     }
202     
203     /* ------------------------------------------------------------ */
204     /** Set the configurationManager.
205      * @param configurationManager the configurationManager to set
206      */
207     public void setConfigurationManager(ConfigurationManager configurationManager)
208     {
209         _configurationManager = configurationManager;
210     }
211     
212     /* ------------------------------------------------------------ */
213     /**
214      * @param configurations The configuration class names.
215      */
216     public void setConfigurationClasses(String[] configurations)
217     {
218         _configurationClasses = configurations==null?null:(String[])configurations.clone();
219     }  
220     
221     /* ------------------------------------------------------------ */
222     /**
223      *
224      */
225     @ManagedAttribute("configuration classes for webapps to be processed through")
226     public String[] getConfigurationClasses()
227     {
228         return _configurationClasses;
229     }
230 
231     /**
232      * Set the Work directory where unpacked WAR files are managed from.
233      * <p>
234      * Default is the same as the <code>java.io.tmpdir</code> System Property.
235      *
236      * @param directory the new work directory
237      */
238     public void setTempDir(File directory)
239     {
240         _tempDirectory = directory;
241     }
242 
243     /**
244      * Get the user supplied Work Directory.
245      *
246      * @return the user supplied work directory (null if user has not set Temp Directory yet)
247      */
248     @ManagedAttribute("temp directory for use, null if no user set temp directory")
249     public File getTempDir()
250     {
251         return _tempDirectory;
252     }
253 
254     /* ------------------------------------------------------------ */
255     @Override
256     public ContextHandler createContextHandler(final App app) throws Exception
257     {
258         Resource resource = Resource.newResource(app.getOriginId());
259         File file = resource.getFile();
260         if (!resource.exists())
261             throw new IllegalStateException("App resouce does not exist "+resource);
262 
263         String context = file.getName();
264 
265         if (resource.exists() && FileID.isXmlFile(file))
266         {
267             XmlConfiguration xmlc = new XmlConfiguration(resource.getURL())
268             {
269                 @Override
270                 public void initializeDefaults(Object context)
271                 {
272                     super.initializeDefaults(context);
273 
274                     if (context instanceof WebAppContext)
275                     {
276                         WebAppContext webapp = (WebAppContext)context;
277                         webapp.setParentLoaderPriority(_parentLoaderPriority);
278                         if (_defaultsDescriptor != null)
279                             webapp.setDefaultsDescriptor(_defaultsDescriptor);
280                     }
281                 }
282             };
283 
284             xmlc.getIdMap().put("Server", getDeploymentManager().getServer());
285             xmlc.getProperties().put("jetty.webapp",file.getCanonicalPath());
286             xmlc.getProperties().put("jetty.webapps",file.getParentFile().getCanonicalPath());
287 
288             if (getConfigurationManager() != null)
289                 xmlc.getProperties().putAll(getConfigurationManager().getProperties());
290             return (ContextHandler)xmlc.configure();
291         }
292         else if (file.isDirectory())
293         {
294             // must be a directory
295         }
296         else if (FileID.isWebArchiveFile(file))
297         {
298             // Context Path is the same as the archive.
299             context = context.substring(0,context.length() - 4);
300         }
301         else
302         {
303             throw new IllegalStateException("unable to create ContextHandler for "+app);
304         }
305 
306         // Ensure "/" is Not Trailing in context paths.
307         if (context.endsWith("/") && context.length() > 0)
308         {
309             context = context.substring(0,context.length() - 1);
310         }
311 
312         // Start building the webapplication
313         WebAppContext webAppContext = new WebAppContext();
314         webAppContext.setDisplayName(context);
315 
316         // special case of archive (or dir) named "root" is / context
317         if (context.equalsIgnoreCase("root"))
318         {
319             context = URIUtil.SLASH;
320         }
321         else if (context.toLowerCase(Locale.ENGLISH).startsWith("root-"))
322         {
323             int dash=context.toLowerCase(Locale.ENGLISH).indexOf('-');
324             String virtual = context.substring(dash+1);
325             webAppContext.setVirtualHosts(new String[]{virtual});
326             context = URIUtil.SLASH;
327         }
328 
329         // Ensure "/" is Prepended to all context paths.
330         if (context.charAt(0) != '/')
331         {
332             context = "/" + context;
333         }
334 
335 
336         webAppContext.setContextPath(context);
337         webAppContext.setWar(file.getAbsolutePath());
338         if (_defaultsDescriptor != null)
339         {
340             webAppContext.setDefaultsDescriptor(_defaultsDescriptor);
341         }
342         webAppContext.setExtractWAR(_extractWars);
343         webAppContext.setParentLoaderPriority(_parentLoaderPriority);
344         if (_configurationClasses != null)
345         {
346             webAppContext.setConfigurationClasses(_configurationClasses);
347         }
348 
349         if (_tempDirectory != null)
350         {
351             /* Since the Temp Dir is really a context base temp directory,
352              * Lets set the Temp Directory in a way similar to how WebInfConfiguration does it,
353              * instead of setting the
354              * WebAppContext.setTempDirectory(File).  
355              * If we used .setTempDirectory(File) all webapps will wind up in the
356              * same temp / work directory, overwriting each others work.
357              */
358             webAppContext.setAttribute(WebAppContext.BASETEMPDIR, _tempDirectory);
359         }
360         return webAppContext;
361     }
362 
363 }