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.io.IOException;
24 import java.net.MalformedURLException;
25 import java.util.Locale;
26
27 import org.eclipse.jetty.deploy.App;
28 import org.eclipse.jetty.deploy.util.FileID;
29 import org.eclipse.jetty.server.handler.ContextHandler;
30 import org.eclipse.jetty.util.URIUtil;
31 import org.eclipse.jetty.util.resource.Resource;
32 import org.eclipse.jetty.webapp.WebAppContext;
33
34 /* ------------------------------------------------------------ */
35 /** Context directory App Provider.
36 * <p>This specialization of {@link ScanningAppProvider} is the
37 * replacement for old (and deprecated) <code>org.eclipse.jetty.deploy.WebAppDeployer</code> and it will scan a directory
38 * only for war files or directories files.</p>
39 * <p>
40 * Webapps with names root or starting with root- are deployed at /.
41 * If the name is in the format root-hostname, then the webapp is deployed
42 * at / in the virtual host hostname.
43 */
44 public class WebAppProvider extends ScanningAppProvider
45 {
46 private boolean _extractWars = false;
47 private boolean _parentLoaderPriority = false;
48 private String _defaultsDescriptor;
49 private Filter _filter;
50 private File _tempDirectory;
51 private String[] _configurationClasses;
52
53 public static class Filter implements FilenameFilter
54 {
55 private File _contexts;
56
57 public boolean accept(File dir, String name)
58 {
59 if (!dir.exists())
60 {
61 return false;
62 }
63 String lowername = name.toLowerCase(Locale.ENGLISH);
64
65 File file = new File(dir,name);
66 // is it not a directory and not a war ?
67 if (!file.isDirectory() && !lowername.endsWith(".war"))
68 {
69 return false;
70 }
71
72 //ignore hidden files
73 if (lowername.startsWith("."))
74 return false;
75
76 if (file.isDirectory())
77 {
78 // is it a directory for an existing war file?
79 if (new File(dir,name+".war").exists() ||
80 new File(dir,name+".WAR").exists())
81
82 return false;
83
84 //is it a sccs dir?
85 if ("cvs".equals(lowername) || "cvsroot".equals(lowername))
86 return false;
87 }
88
89 // is there a contexts config file
90 if (_contexts!=null)
91 {
92 String context=name;
93 if (!file.isDirectory())
94 {
95 context=context.substring(0,context.length()-4);
96 }
97 if (new File(_contexts,context+".xml").exists() ||
98 new File(_contexts,context+".XML").exists() )
99 {
100 return false;
101 }
102 }
103
104 return true;
105 }
106 }
107
108 /* ------------------------------------------------------------ */
109 public WebAppProvider()
110 {
111 super(new Filter());
112 _filter=(Filter)_filenameFilter;
113 setScanInterval(0);
114 }
115
116 /* ------------------------------------------------------------ */
117 /** Get the extractWars.
118 * @return the extractWars
119 */
120 public boolean isExtractWars()
121 {
122 return _extractWars;
123 }
124
125 /* ------------------------------------------------------------ */
126 /** Set the extractWars.
127 * @param extractWars the extractWars to set
128 */
129 public void setExtractWars(boolean extractWars)
130 {
131 _extractWars = extractWars;
132 }
133
134 /* ------------------------------------------------------------ */
135 /** Get the parentLoaderPriority.
136 * @return the parentLoaderPriority
137 */
138 public boolean isParentLoaderPriority()
139 {
140 return _parentLoaderPriority;
141 }
142
143 /* ------------------------------------------------------------ */
144 /** Set the parentLoaderPriority.
145 * @param parentLoaderPriority the parentLoaderPriority to set
146 */
147 public void setParentLoaderPriority(boolean parentLoaderPriority)
148 {
149 _parentLoaderPriority = parentLoaderPriority;
150 }
151
152 /* ------------------------------------------------------------ */
153 /** Get the defaultsDescriptor.
154 * @return the defaultsDescriptor
155 */
156 public String getDefaultsDescriptor()
157 {
158 return _defaultsDescriptor;
159 }
160
161 /* ------------------------------------------------------------ */
162 /** Set the defaultsDescriptor.
163 * @param defaultsDescriptor the defaultsDescriptor to set
164 */
165 public void setDefaultsDescriptor(String defaultsDescriptor)
166 {
167 _defaultsDescriptor = defaultsDescriptor;
168 }
169
170 /* ------------------------------------------------------------ */
171 public String getContextXmlDir()
172 {
173 return _filter._contexts==null?null:_filter._contexts.toString();
174 }
175
176 /* ------------------------------------------------------------ */
177 /**
178 * Set the directory in which to look for context XML files.
179 * <p>
180 * If a webapp call "foo/" or "foo.war" is discovered in the monitored
181 * directory, then the ContextXmlDir is examined to see if a foo.xml
182 * file exists. If it does, then this deployer will not deploy the webapp
183 * and the ContextProvider should be used to act on the foo.xml file.
184 * @see ContextProvider
185 * @param contextsDir
186 */
187 public void setContextXmlDir(String contextsDir)
188 {
189 try
190 {
191 _filter._contexts=Resource.newResource(contextsDir).getFile();
192 }
193 catch (MalformedURLException e)
194 {
195 throw new RuntimeException(e);
196 }
197 catch (IOException e)
198 {
199 throw new RuntimeException(e);
200 }
201 }
202
203
204 /* ------------------------------------------------------------ */
205 /**
206 * @param configurations The configuration class names.
207 */
208 public void setConfigurationClasses(String[] configurations)
209 {
210 _configurationClasses = configurations==null?null:(String[])configurations.clone();
211 }
212
213 /* ------------------------------------------------------------ */
214 /**
215 *
216 */
217 public String[] getConfigurationClasses()
218 {
219 return _configurationClasses;
220 }
221
222 /**
223 * Set the Work directory where unpacked WAR files are managed from.
224 * <p>
225 * Default is the same as the <code>java.io.tmpdir</code> System Property.
226 *
227 * @param directory the new work directory
228 */
229 public void setTempDir(File directory)
230 {
231 _tempDirectory = directory;
232 }
233
234 /**
235 * Get the user supplied Work Directory.
236 *
237 * @return the user supplied work directory (null if user has not set Temp Directory yet)
238 */
239 public File getTempDir()
240 {
241 return _tempDirectory;
242 }
243
244 /* ------------------------------------------------------------ */
245 public ContextHandler createContextHandler(final App app) throws Exception
246 {
247 Resource resource = Resource.newResource(app.getOriginId());
248 File file = resource.getFile();
249 if (!resource.exists())
250 throw new IllegalStateException("App resouce does not exist "+resource);
251
252 String context = file.getName();
253
254 if (file.isDirectory())
255 {
256 // must be a directory
257 }
258 else if (FileID.isWebArchiveFile(file))
259 {
260 // Context Path is the same as the archive.
261 context = context.substring(0,context.length() - 4);
262 }
263 else
264 {
265 throw new IllegalStateException("unable to create ContextHandler for "+app);
266 }
267
268 // Ensure "/" is Not Trailing in context paths.
269 if (context.endsWith("/") && context.length() > 0)
270 {
271 context = context.substring(0,context.length() - 1);
272 }
273
274 // Start building the webapplication
275 WebAppContext wah = new WebAppContext();
276 wah.setDisplayName(context);
277
278 // special case of archive (or dir) named "root" is / context
279 if (context.equalsIgnoreCase("root"))
280 {
281 context = URIUtil.SLASH;
282 }
283 else if (context.toLowerCase(Locale.ENGLISH).startsWith("root-"))
284 {
285 int dash=context.toLowerCase(Locale.ENGLISH).indexOf('-');
286 String virtual = context.substring(dash+1);
287 wah.setVirtualHosts(new String[]{virtual});
288 context = URIUtil.SLASH;
289 }
290
291 // Ensure "/" is Prepended to all context paths.
292 if (context.charAt(0) != '/')
293 {
294 context = "/" + context;
295 }
296
297
298 wah.setContextPath(context);
299 wah.setWar(file.getAbsolutePath());
300 if (_defaultsDescriptor != null)
301 {
302 wah.setDefaultsDescriptor(_defaultsDescriptor);
303 }
304 wah.setExtractWAR(_extractWars);
305 wah.setParentLoaderPriority(_parentLoaderPriority);
306 if (_configurationClasses != null)
307 {
308 wah.setConfigurationClasses(_configurationClasses);
309 }
310
311 if (_tempDirectory != null)
312 {
313 /* Since the Temp Dir is really a context base temp directory,
314 * Lets set the Temp Directory in a way similar to how WebInfConfiguration does it,
315 * instead of setting the
316 * WebAppContext.setTempDirectory(File).
317 * If we used .setTempDirectory(File) all webapps will wind up in the
318 * same temp / work directory, overwriting each others work.
319 */
320 wah.setAttribute(WebAppContext.BASETEMPDIR,_tempDirectory);
321 }
322 return wah;
323 }
324
325 }