View Javadoc

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