View Javadoc

1   // ========================================================================
2   // Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.deploy;
15  
16  import java.io.File;
17  import java.io.FilenameFilter;
18  import java.util.HashMap;
19  import java.util.Map;
20  
21  import org.eclipse.jetty.server.Server;
22  import org.eclipse.jetty.server.handler.ContextHandler;
23  import org.eclipse.jetty.server.handler.ContextHandlerCollection;
24  import org.eclipse.jetty.util.AttributesMap;
25  import org.eclipse.jetty.util.Scanner;
26  import org.eclipse.jetty.util.component.AbstractLifeCycle;
27  import org.eclipse.jetty.util.log.Log;
28  import org.eclipse.jetty.util.resource.Resource;
29  import org.eclipse.jetty.xml.XmlConfiguration;
30  
31  /**
32   * Legacy Context Deployer.
33   * 
34   * <p>
35   * Note: The WebAppDeployer is being phased out of Jetty in favor of the {@link DeploymentManager} and
36   * {@link org.eclipse.jetty.deploy.providers.ContextProvider} implementation.
37   * 
38   * <p>
39   * This deployer scans a designated directory by {@link #setConfigurationDir(String)} for the appearance/disappearance
40   * or changes to xml configuration files. The scan is performed at startup and at an optional hot deployment frequency
41   * specified by {@link #setScanInterval(int)}. By default, the scanning is NOT recursive, but can be made so by
42   * {@link #setRecursive(boolean)}.
43   * 
44   * <p>
45   * Each configuration file is in {@link XmlConfiguration} format and represents the configuration of a instance of
46   * {@link ContextHandler} (or a subclass specified by the XML <code>Configure</code> element).
47   * 
48   * <p>
49   * The xml should configure the context and the instance is deployed to the {@link ContextHandlerCollection} specified
50   * by {@link Server#setHandler(org.eclipse.jetty.server.Handler)}.
51   * 
52   * <p>
53   * Similarly, when one of these existing files is removed, the corresponding context is undeployed; when one of these
54   * files is changed, the corresponding context is undeployed, the (changed) xml config file reapplied to it, and then
55   * (re)deployed.
56   * 
57   * <p>
58   * Note that the context itself is NOT copied into the hot deploy directory. The webapp directory or war file can exist
59   * anywhere. It is the xml config file that points to it's location and deploys it from there.
60   * 
61   * <p>
62   * It means, for example, that you can keep a "read-only" copy of your webapp somewhere, and apply different
63   * configurations to it simply by dropping different xml configuration files into the configuration directory.
64   * 
65   * @see DeploymentManager
66   * @see MonitoredDirAppProvider
67   * 
68   * @org.apache.xbean.XBean element="hotDeployer" description="Creates a hot deployer to watch a directory for changes at
69   *                         a configurable interval."
70   */
71  @SuppressWarnings("unchecked")
72  @Deprecated
73  public class ContextDeployer extends AbstractLifeCycle
74  {
75      private int _scanInterval=10;
76      private Scanner _scanner;
77      private ScannerListener _scannerListener;
78      private Resource _contextsDir;
79      private Map _currentDeployments = new HashMap();
80      private ContextHandlerCollection _contexts;
81      private ConfigurationManager _configMgr;
82      private boolean _recursive = false;
83      private AttributesMap _contextAttributes = new AttributesMap();
84      
85      
86      /* ------------------------------------------------------------ */
87      protected class ScannerListener implements Scanner.DiscreteListener
88      {
89          /**
90           * Handle a new deployment
91           * 
92           * @see org.eclipse.jetty.util.Scanner.DiscreteListener#fileAdded(java.lang.String)
93           */
94          public void fileAdded(String filename) throws Exception
95          {
96              deploy(filename);
97          }
98  
99          /**
100          * Handle a change to an existing deployment. Undeploy then redeploy.
101          * 
102          * @see org.eclipse.jetty.util.Scanner.DiscreteListener#fileChanged(java.lang.String)
103          */
104         public void fileChanged(String filename) throws Exception
105         {
106             redeploy(filename);
107         }
108 
109         /**
110          * Handle an undeploy.
111          * 
112          * @see org.eclipse.jetty.util.Scanner.DiscreteListener#fileRemoved(java.lang.String)
113          */
114         public void fileRemoved(String filename) throws Exception
115         {
116             undeploy(filename);
117         }
118         @Override
119         public String toString()
120         {
121             return "ContextDeployer$Scanner";
122         }
123     }
124 
125     /**
126      * Constructor
127      */
128     public ContextDeployer() 
129     {
130         Log.warn("ContextDeployer is deprecated. Use ContextProvider");
131         _scanner=new Scanner();
132     }
133 
134     /* ------------------------------------------------------------ */
135     /**
136      * @return the ContextHandlerColletion to which to deploy the contexts
137      */
138     public ContextHandlerCollection getContexts()
139     {
140         return _contexts;
141     }
142 
143     /* ------------------------------------------------------------ */
144     /**
145      * Associate with a {@link ContextHandlerCollection}.
146      * 
147      * @param contexts
148      *            the ContextHandlerColletion to which to deploy the contexts
149      */
150     public void setContexts(ContextHandlerCollection contexts)
151     {
152         if (isStarted()||isStarting())
153             throw new IllegalStateException("Cannot set Contexts after deployer start");
154         _contexts=contexts;
155     }
156 
157     /* ------------------------------------------------------------ */
158     /**
159      * @param seconds
160      *            The period in second between scans for changed configuration
161      *            files. A zero or negative interval disables hot deployment
162      */
163     public void setScanInterval(int seconds)
164     {
165         if (isStarted()||isStarting())
166             throw new IllegalStateException("Cannot change scan interval after deployer start");
167         _scanInterval=seconds;
168     }
169 
170     /* ------------------------------------------------------------ */
171     public int getScanInterval()
172     {
173         return _scanInterval;
174     }
175 
176     /* ------------------------------------------------------------ */
177     /**
178      * @param dir Directory to scan for context descriptors
179      */
180     public void setContextsDir(String dir)
181     {
182         try
183         {
184             _contextsDir=Resource.newResource(dir);
185         }
186         catch(Exception e)
187         {
188             throw new IllegalArgumentException(e);
189         }
190     }
191 
192     /* ------------------------------------------------------------ */
193     public String getContextsDir()
194     {
195         return _contextsDir==null?null:_contextsDir.toString();
196     }
197 
198     /* ------------------------------------------------------------ */
199     /**
200      * @param dir
201      * @throws Exception
202      * @deprecated use {@link #setContextsDir(String)}
203      */
204     @Deprecated
205     public void setConfigurationDir(String dir) throws Exception
206     {
207         setConfigurationDir(Resource.newResource(dir));
208     }
209 
210     /* ------------------------------------------------------------ */
211     /**
212      * @param file
213      * @throws Exception
214      * @deprecated use {@link #setContextsDir(String)}
215      */
216     @Deprecated
217     public void setConfigurationDir(File file) throws Exception
218     {
219         setConfigurationDir(Resource.newResource(file.toURL()));
220     }
221 
222     /* ------------------------------------------------------------ */
223     /**
224      * @param resource
225      * @deprecated use {@link #setContextsDir(String)}
226      */
227     @Deprecated
228     public void setConfigurationDir(Resource resource)
229     {
230         if (isStarted()||isStarting())
231             throw new IllegalStateException("Cannot change hot deploy dir after deployer start");
232         _contextsDir=resource;
233     }
234 
235     /* ------------------------------------------------------------ */
236     /**
237      * @param directory
238      * @deprecated use {@link #setContextsDir(String)}
239      */
240     @Deprecated
241     public void setDirectory(String directory) throws Exception
242     {
243         setConfigurationDir(directory);
244     }
245     
246     /* ------------------------------------------------------------ */
247     /**
248      * @return the directory
249      * @deprecated use {@link #setContextsDir(String)}
250      */
251     @Deprecated
252     public String getDirectory()
253     {
254         return getConfigurationDir().getName();
255     }
256 
257     /* ------------------------------------------------------------ */
258     /**
259      * @return the configuration directory
260      * @deprecated use {@link #setContextsDir(String)}
261      */
262     @Deprecated
263     public Resource getConfigurationDir()
264     {
265         return _contextsDir;
266     }
267 
268     /* ------------------------------------------------------------ */
269     /**
270      * @param configMgr
271      */
272     public void setConfigurationManager(ConfigurationManager configMgr)
273     {
274         _configMgr=configMgr;
275     }
276 
277     /* ------------------------------------------------------------ */
278     /**
279      * @return the configuration manager
280      */
281     public ConfigurationManager getConfigurationManager()
282     {
283         return _configMgr;
284     }
285 
286 
287     /* ------------------------------------------------------------ */
288     public void setRecursive (boolean recursive)
289     {
290         _recursive=recursive;
291     }
292 
293     /* ------------------------------------------------------------ */
294     public boolean getRecursive ()
295     {
296         return _recursive;
297     }
298 
299     /* ------------------------------------------------------------ */
300     public boolean isRecursive()
301     {
302         return _recursive;
303     }
304     
305 
306     /* ------------------------------------------------------------ */
307     /**
308      * Set a contextAttribute that will be set for every Context deployed by this deployer.
309      * @param name
310      * @param value
311      */
312     public void setAttribute (String name, Object value)
313     {
314         _contextAttributes.setAttribute(name,value);
315     }
316     
317 
318     /* ------------------------------------------------------------ */
319     /**
320      * Get a contextAttribute that will be set for every Context deployed by this deployer.
321      * @param name
322      * @return the attribute value
323      */
324     public Object getAttribute (String name)
325     {
326         return _contextAttributes.getAttribute(name);
327     }
328     
329 
330     /* ------------------------------------------------------------ */
331     /**
332      * Remove a contextAttribute that will be set for every Context deployed by this deployer.
333      * @param name
334      */
335     public void removeAttribute(String name)
336     {
337         _contextAttributes.removeAttribute(name);
338     }
339 
340     /* ------------------------------------------------------------ */
341     private void deploy(String filename) throws Exception
342     {
343         ContextHandler context=createContext(filename);
344         Log.info("Deploy "+filename+" -> "+ context);
345         _contexts.addHandler(context);
346         _currentDeployments.put(filename,context);
347         if (_contexts.isStarted())
348             context.start();
349     }
350 
351     /* ------------------------------------------------------------ */
352     private void undeploy(String filename) throws Exception
353     {
354         ContextHandler context=(ContextHandler)_currentDeployments.get(filename);
355         Log.info("Undeploy "+filename+" -> "+context);
356         if (context==null)
357             return;
358         context.stop();
359         _contexts.removeHandler(context);
360         _currentDeployments.remove(filename);
361     }
362 
363     /* ------------------------------------------------------------ */
364     private void redeploy(String filename) throws Exception
365     {
366         undeploy(filename);
367         deploy(filename);
368     }
369 
370     /* ------------------------------------------------------------ */
371     /**
372      * Start the hot deployer looking for webapps to deploy/undeploy
373      * 
374      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
375      */
376     @SuppressWarnings("deprecation")
377     @Override
378     protected void doStart() throws Exception
379     {
380         if (_contextsDir==null)
381             throw new IllegalStateException("No configuration dir specified");
382 
383         if (_contexts==null)
384             throw new IllegalStateException("No context handler collection specified for deployer");
385 
386         _scanner.setScanDir(_contextsDir.getFile());
387         _scanner.setScanInterval(getScanInterval());
388         _scanner.setRecursive(_recursive); //only look in the top level for deployment files?
389         // Accept changes only in files that could be a deployment descriptor
390         _scanner.setFilenameFilter(new FilenameFilter()
391         {
392             public boolean accept(File dir, String name)
393             {
394                 try
395                 {
396                     if (name.endsWith(".xml"))
397                         return true;
398                     return false;
399                 }
400                 catch (Exception e)
401                 {
402                     Log.warn(e);
403                     return false;
404                 }
405             }
406         });
407         _scannerListener=new ScannerListener();
408         _scanner.addListener(_scannerListener);
409         _scanner.scan();
410         _scanner.start();
411         _contexts.getServer().getContainer().addBean(_scanner);
412     }
413 
414     /* ------------------------------------------------------------ */
415     /**
416      * Stop the hot deployer.
417      * 
418      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
419      */
420     @Override
421     protected void doStop() throws Exception
422     {
423         _scanner.removeListener(_scannerListener);
424         _scanner.stop();
425     }
426 
427     /* ------------------------------------------------------------ */
428     /**
429      * Create a WebAppContext for the webapp being hot deployed, then apply the
430      * xml config file to it to configure it.
431      * 
432      * @param filename
433      *            the config file found in the hot deploy directory
434      * @return
435      * @throws Exception
436      */
437     private ContextHandler createContext(String filename) throws Exception
438     {
439         // The config file can call any method on WebAppContext to configure
440         // the webapp being deployed.
441         Resource resource = Resource.newResource(filename);
442         if (!resource.exists())
443             return null;
444 
445         XmlConfiguration xmlConfiguration=new XmlConfiguration(resource.getURL());
446         HashMap properties = new HashMap();
447         properties.put("Server", _contexts.getServer());
448         if (_configMgr!=null)
449             properties.putAll(_configMgr.getProperties());
450            
451         xmlConfiguration.setProperties(properties);
452         ContextHandler context=(ContextHandler)xmlConfiguration.configure();
453         
454         // merge attributes
455         if (_contextAttributes!=null && _contextAttributes.size()>0)
456         {
457             AttributesMap attributes = new AttributesMap(_contextAttributes);
458             attributes.addAll(context.getAttributes());
459             context.setAttributes(attributes);
460         }
461         return context;
462     }
463 
464 }