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