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