View Javadoc

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