KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > deployment > scanner > URLDirectoryScanner


1 /***************************************
2  * *
3  * JBoss: The OpenSource J2EE WebOS *
4  * *
5  * Distributable under LGPL license. *
6  * See terms of license at gnu.org. *
7  * *
8  ***************************************/

9
10 package org.jboss.deployment.scanner;
11
12 import java.io.File JavaDoc;
13 import java.io.FileFilter JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.net.MalformedURLException JavaDoc;
16 import java.net.URL JavaDoc;
17 import java.net.URLConnection JavaDoc;
18 import java.util.Arrays JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Comparator JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import javax.management.MBeanServer JavaDoc;
24 import javax.management.ObjectName JavaDoc;
25 import org.w3c.dom.Element JavaDoc;
26 import org.w3c.dom.NamedNodeMap JavaDoc;
27 import org.w3c.dom.Node JavaDoc;
28 import org.w3c.dom.NodeList JavaDoc;
29
30 import org.jboss.deployment.Deployer;
31 import org.jboss.deployment.DeploymentException;
32 import org.jboss.system.server.ServerConfigLocator;
33
34 /**
35  * This class is similar to the URLDeploymentScanner (
36  * @see org.jboss.deployment.scanner.URLDeploymentScanner). It is designed
37  * to deploy direct URLs and to scan directories. The distinction between
38  * the two is that this class forces the user to specify which entries are
39  * directories to scan, and which are urls to deploy.
40  *
41  * @jmx:mbean extends="org.jboss.deployment.scanner.DeploymentScannerMBean"
42  *
43  * @version
44  * @author <a HREF="mailto:lsanders@speakeasy.net">Larry Sanderson</a>
45  */

46 public class URLDirectoryScanner extends AbstractDeploymentScanner
47    implements DeploymentScanner, URLDirectoryScannerMBean
48 {
49    
50    /** This is the default file filter for directory scanning */
51    private FileFilter JavaDoc defaultFilter;
52    
53    /** This is the default Comparator for directory deployment ordering */
54    private Comparator JavaDoc defaultComparator;
55    
56    /** This is the list of scanner objects */
57    private ArrayList JavaDoc scanners = new ArrayList JavaDoc();
58    
59    /** This is a URL to scanner map. */
60    private HashMap JavaDoc urlScannerMap = new HashMap JavaDoc();
61    
62    /** this is used for resolution of reletive file paths */
63    private File JavaDoc serverHome;
64    
65    /**
66     * @jmx:managed-attribute
67     */

68    public void addScanURL(URL JavaDoc url)
69    {
70       Scanner scanner = new Scanner(url);
71       addScanner(scanner);
72    }
73    
74    /**
75     * @jmx:managed-attribute
76     */

77    public void addScanURL(String JavaDoc url) throws MalformedURLException JavaDoc
78    {
79       addScanURL(toUrl(url));
80    }
81    
82    /**
83     * @jmx:managed-attribute
84     */

85    public void addScanDir(URL JavaDoc url, Comparator JavaDoc comp, FileFilter JavaDoc filter)
86    {
87       Scanner scanner = new DirScanner(url, comp, filter);
88       addScanner(scanner);
89    }
90    
91    /**
92     * @jmx:managed-attribute
93     */

94    public void addScanDir(String JavaDoc urlSpec, String JavaDoc compClassName, String JavaDoc filterClassName)
95       throws MalformedURLException JavaDoc
96    {
97       
98       URL JavaDoc url = toUrl(urlSpec);
99       // create a new comparator
100
Comparator JavaDoc comp = null;
101       if (compClassName != null)
102       {
103          try
104          {
105             Class JavaDoc compClass = Thread.currentThread().getContextClassLoader().loadClass("compClassName");
106             comp = (Comparator JavaDoc)compClass.newInstance();
107          } catch (Exception JavaDoc e)
108          {
109             log.warn("Unable to create instance of Comparator. Ignoring.", e);
110          }
111       }
112       // create a new filter
113
FileFilter JavaDoc filter = null;
114       if (filterClassName != null)
115       {
116          try
117          {
118             Class JavaDoc filterClass = Thread.currentThread().getContextClassLoader().loadClass(filterClassName);
119             filter = (FileFilter JavaDoc)filterClass.newInstance();
120          } catch (Exception JavaDoc e)
121          {
122             log.warn("Unable to create instance of Filter. Ignoring.", e);
123          }
124       }
125       
126       addScanDir(url, comp, filter);
127    }
128    
129    private void addScanner(Scanner scanner)
130    {
131       synchronized (scanners)
132       {
133          if (isScanEnabled())
134          {
135             // Scan is enabled, so apply changes to a local copy
136
// this enables the scan to occur while things are added
137
ArrayList JavaDoc localScanners = new ArrayList JavaDoc(scanners);
138             HashMap JavaDoc localMap = new HashMap JavaDoc(urlScannerMap);
139             
140             localScanners.add(scanner);
141             localMap.put(scanner.url, scanner);
142             
143             scanners = localScanners;
144             urlScannerMap = localMap;
145          } else
146          {
147             // no need for precautions... just add
148
scanners.add(scanner);
149             urlScannerMap.put(scanner.url, scanner);
150          }
151       }
152    }
153    
154    /**
155     * @jmx:managed-attribute
156     */

157    public void removeScanURL(URL JavaDoc url)
158    {
159       synchronized (scanners)
160       {
161          if (isScanEnabled())
162          {
163             // Scan is enabled, so apply changes to a local copy
164
// this enables the scan to occur while things are added
165
ArrayList JavaDoc localScanners = new ArrayList JavaDoc(scanners);
166             HashMap JavaDoc localMap = new HashMap JavaDoc(urlScannerMap);
167             
168             Scanner scanner = (Scanner)localMap.remove(url);
169             if (scanner != null)
170             {
171                localScanners.remove(scanner);
172             }
173             
174             scanners = localScanners;
175             urlScannerMap = localMap;
176          } else
177          {
178             // no need for precautions... just remove
179
Scanner scanner = (Scanner)urlScannerMap.remove(url);
180             if (scanner != null)
181             {
182                scanners.remove(scanner);
183             }
184          }
185       }
186    }
187    
188    /**
189     * @jmx:managed-attribute
190     */

191    public void setURLs(Element JavaDoc elem)
192    {
193       // create local versions of these collections
194
ArrayList JavaDoc localScanners = new ArrayList JavaDoc();
195       HashMap JavaDoc localMap = new HashMap JavaDoc();
196       
197       NodeList JavaDoc list = elem.getChildNodes();
198       synchronized (scanners)
199       {
200          // clear lists
201
scanners.clear();
202          urlScannerMap.clear();
203          
204          // populate from xml....
205
for (int i = 0; i < list.getLength(); i++)
206          {
207             Node JavaDoc node = list.item(i);
208             if (node.getNodeType() == Node.ELEMENT_NODE) {
209                NamedNodeMap JavaDoc nodeMap = node.getAttributes();
210                String JavaDoc name = getNodeValue(nodeMap.getNamedItem("name"));
211                if (name == null)
212                {
213                   log.warn("No name specified in URLDirectoryScanner config: " +
214                   node + ". Ignoring");
215                   continue;
216                }
217
218                try
219                {
220                   if (node.getNodeName().equals("dir"))
221                   {
222                      // get the filter and comparator
223
String JavaDoc filter = getNodeValue(nodeMap.getNamedItem("filter"));
224                      String JavaDoc comp = getNodeValue(nodeMap.getNamedItem("comparator"));
225
226                      addScanDir(name, comp, filter);
227                   } else if (node.getNodeName().equals("url"))
228                   {
229                      addScanURL(name);
230                   }
231                } catch (MalformedURLException JavaDoc e)
232                {
233                   log.warn("Invalid url in DeploymentScanner. Ignoring.", e);
234                }
235             }
236          }
237       }
238    }
239    
240    /**
241     * Extract a string from a node. Trim the value (down to null for empties).
242     */

243    private String JavaDoc getNodeValue(Node JavaDoc node)
244    {
245       if (node == null)
246       {
247          return null;
248       }
249       String JavaDoc val = node.getNodeValue();
250       if (val == null)
251       {
252          return null;
253       }
254       if ((val = val.trim()).length() == 0)
255       {
256          return null;
257       }
258       return val;
259    }
260    
261    /**
262     * Convert a string to a node. I really just copied the code from
263     * Jason Dillon's URLDeploymentScanner. Thanks Jason!
264     */

265    private URL JavaDoc toUrl(String JavaDoc value) throws MalformedURLException JavaDoc
266    {
267       try
268       {
269          return new URL JavaDoc(value);
270       } catch (MalformedURLException JavaDoc e)
271       {
272          File JavaDoc file = new File JavaDoc(value);
273          if (!file.isAbsolute())
274          {
275             file = new File JavaDoc(serverHome, value);
276          }
277          
278          try
279          {
280             file = file.getCanonicalFile();
281          } catch (IOException JavaDoc ioe)
282          {
283             throw new MalformedURLException JavaDoc("Can not obtain file: " + ioe);
284          }
285          
286          return file.toURL();
287       }
288    }
289    
290    /**
291     * @jmx:managed-attribute
292     */

293    public void setURLComparator(String JavaDoc comparatorClassName)
294    {
295       log.debug("Setting Comparator: " + comparatorClassName);
296       try
297       {
298          defaultComparator = (Comparator JavaDoc)Thread.currentThread().getContextClassLoader().loadClass(comparatorClassName).newInstance();
299       } catch (Exception JavaDoc e)
300       {
301          log.warn("Unable to create URLComparator.", e);
302       }
303    }
304    
305    /**
306     * @jmx:managed-attribute
307     */

308    public String JavaDoc getURLComparator()
309    {
310       if (defaultComparator == null)
311       {
312          return null;
313       }
314       return defaultComparator.getClass().getName();
315    }
316    
317    /**
318     * @jmx:managed-attribute
319     */

320    public void setFilter(String JavaDoc filterClassName)
321    {
322       log.debug("Setting Filter: " + filterClassName);
323       try
324       {
325          defaultFilter = (FileFilter JavaDoc)Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
326       } catch (Exception JavaDoc e)
327       {
328          log.warn("Unable to create URLComparator.", e);
329       }
330    }
331    
332    /**
333     * @jmx:managed-attribute
334     */

335    public String JavaDoc getFilter()
336    {
337       if (defaultFilter == null)
338       {
339          return null;
340       }
341       return defaultFilter.getClass().getName();
342    }
343    
344    /**
345     * This is a workaround for a bug in Sun's JVM 1.3 on windows (any
346     * others??). Inner classes can not directly access protected members
347     * from the outer-class's super class.
348     */

349    Deployer getDeployerObj()
350    {
351       return deployer;
352    }
353    
354    /**
355     * This class scans a single url for modifications. It supports
356     * missing url's, and will deploy them when they appear.
357     */

358    private class Scanner
359    {
360       /** the original url to scan */
361       protected URL JavaDoc url;
362       
363       /** the url to watch for modification */
364       private URL JavaDoc watchUrl;
365       
366       /** this holds the lastModified time of watchUrl */
367       private long lastModified;
368       
369       /** this is a flag to indicate if this url is deployed */
370       private boolean deployed;
371       
372       /**
373        * Construct with the url to deploy / watch
374        */

375       public Scanner(URL JavaDoc url)
376       {
377          this.url = url;
378       }
379       
380       /**
381        * Check the url for modification, and deploy / redeploy / undeploy
382        * appropriately.
383        */

384       public void scan()
385       {
386          if (getLog().isTraceEnabled()) {
387             getLog().trace("Scanning url: " + url);
388          }
389          // check time stamps
390
if (lastModified != getLastModified())
391          {
392             if (exists())
393             {
394                // url needs deploy / redeploy
395
try
396                {
397                   getLog().debug("Deploying Modified (or new) url: " + url);
398                   deploy();
399                } catch (DeploymentException e)
400                {
401                   getLog().error("Failed to (re)deploy url: " + url, e);
402                }
403             } else
404             {
405                // url does not exist... undeploy
406
try
407                {
408                   getLog().debug("Undeploying url: " + url);
409                   undeploy();
410                } catch (DeploymentException e)
411                {
412                   getLog().error("Failed to undeploy url: " + url, e);
413                }
414             }
415          }
416       }
417       
418       /**
419        * Return true if the url exists, false otherwise.
420        */

421       private boolean exists()
422       {
423          try
424          {
425             if (url.getProtocol().equals("file")) {
426                File JavaDoc file = new File JavaDoc(url.getPath());
427                return file.exists();
428             } else {
429                url.openStream().close();
430                return true;
431             }
432          } catch (IOException JavaDoc e)
433          {
434             return false;
435          }
436       }
437       /**
438        * return the modification date of watchUrl
439        */

440       private long getLastModified()
441       {
442          try
443          {
444             URL JavaDoc lmUrl = watchUrl == null ? url : watchUrl;
445             return lmUrl.openConnection().getLastModified();
446          } catch (IOException JavaDoc e)
447          {
448             return 0L;
449          }
450       }
451       /**
452        * (Re)deploy the url. This will undeploy the url first, if it is
453        * already deployed. It also fetches
454        */

455       private void deploy() throws DeploymentException
456       {
457          if (deployed)
458          {
459             // already deployed... undeploy first
460
getDeployerObj().undeploy(url);
461          }
462          getDeployerObj().deploy(url);
463          
464          // reset the watch url
465
try
466          {
467             Object JavaDoc o = getServer().invoke(getDeployer(), "getWatchUrl",
468             new Object JavaDoc[]
469             { url },
470             new String JavaDoc[]
471             { URL JavaDoc.class.getName() });
472             watchUrl = o == null ? url : (URL JavaDoc)o;
473             
474             getLog().debug("Watch URL for: " + url + " -> " + watchUrl);
475          } catch (Exception JavaDoc e)
476          {
477             watchUrl = url;
478             getLog().debug("Unable to obtain watchUrl from deployer. " +
479             "Use url: " + url, e);
480          }
481          
482          // the watchurl may have changed... get a new lastModified
483
lastModified = getLastModified();
484          
485          // set the deployed flag
486
deployed = true;
487       }
488       /**
489        * Undeploy the url (if deployed).
490        */

491       private void undeploy() throws DeploymentException
492       {
493          if (!deployed)
494          {
495             return;
496          }
497          getDeployerObj().undeploy(url);
498          // reset the other fields
499
deployed = false;
500          lastModified = 0L;
501          watchUrl = null;
502       }
503    }
504    
505    /**
506     * This scanner scans a full directory instead of a single file.
507     */

508    private class DirScanner extends Scanner
509    {
510       /** the directory to scan */
511       private File JavaDoc dir;
512       
513       /** the filter to use while scanning */
514       private FileFilter JavaDoc filter;
515       
516       /** The comparator for deployment ordering */
517       private Comparator JavaDoc comp;
518       
519       /** The list of currently deployed Scanners */
520       private ArrayList JavaDoc deployed = new ArrayList JavaDoc();
521       
522       /** Set up the URL, filter, and comparator to use for this scanner */
523       public DirScanner(URL JavaDoc url, Comparator JavaDoc comp, FileFilter JavaDoc filter)
524       {
525          super(url);
526          if (!url.getProtocol().equals("file"))
527          {
528             throw new IllegalArgumentException JavaDoc("Urls for directory " +
529             "scanning must use the file: protocol.");
530          }
531          dir = new File JavaDoc(url.getPath()).getAbsoluteFile();
532          
533          this.filter = filter == null ? defaultFilter : filter;
534          this.comp = new FileComparator(comp == null ? defaultComparator : comp);
535       }
536       
537       /**
538        * Scan the directory for modifications / additions / removals.
539        */

540       public void scan()
541       {
542          // check the dir for existence and file-type
543
if (!dir.exists())
544          {
545             getLog().warn("The director to scan does not exist: " + dir);
546             return;
547          }
548          if (!dir.isDirectory())
549          {
550             getLog().warn("The directory to scan is not a directory: " + dir);
551             return;
552          }
553          
554          // get a sorted list of files in the directory
555
File JavaDoc[] files;
556          if (filter == null)
557          {
558             files = dir.listFiles();
559          } else
560          {
561             files = dir.listFiles(filter);
562          }
563          Arrays.sort(files, comp);
564          
565          // step through the two sorted lists: files and deployed
566
int deployedIndex = 0;
567          int fileIndex = 0;
568          
569          while (true)
570          {
571             // get the current scanner and file
572
Scanner scanner = null;
573             if (deployedIndex < deployed.size())
574             {
575                scanner = (Scanner)deployed.get(deployedIndex);
576             }
577             File JavaDoc file = null;
578             if (fileIndex < files.length)
579             {
580                file = files[fileIndex];
581             }
582             
583             // if both scanner and file are null, we are done
584
if (scanner == null && file == null)
585             {
586                break;
587             }
588             
589             // determine if this is a new / old / or removed file, and
590
// take the appropriate action
591
switch (comp.compare(file, scanner == null ? null : scanner.url))
592             {
593                case -1: // the file is new. Create the scanner
594
getLog().debug("Found new deployable application in directory " +
595                      dir + ": " + file);
596                   try {
597                      scanner = new Scanner(file.toURL());
598                      deployed.add(deployedIndex, scanner);
599                   } catch (MalformedURLException JavaDoc e) {
600                      getLog().warn("Unable to obtain URL for file: " + file, e);
601                      fileIndex++;
602                   }
603                   // do not break! Intentionally fall through to normal scan.
604
case 0: // the file is already deployed. Scan it.
605
scanner.scan();
606                   deployedIndex++;
607                   fileIndex++;
608                   break;
609                case 1: // the file has been removed. A normal scan will remove it
610
getLog().debug("Deployed application removed from directory " +
611                      dir + ": " + file);
612                   scanner.scan();
613                   if (!scanner.deployed)
614                   {
615                      // the undeploy succeded... remove from deployed list
616
deployed.remove(deployedIndex);
617                   } else
618                   {
619                      deployedIndex++;
620                   }
621                   break;
622             }
623          }
624       }
625    }
626    
627    /**
628     * This comparator is used by the dirScanner. It compares two url's
629     * (or Files) using the specified urlComparator. In the case of a tie,
630     * it then uses File's natural ordering. Finally, it normalizes all
631     * compare values so that "less-than" is always -1, "greater-than" is
632     * always 1, and "equals" is always 0.
633     */

634    private static class FileComparator implements Comparator JavaDoc
635    {
636       /** the delegated URL comparator */
637       private Comparator JavaDoc urlComparator;
638       
639       /** Construct with a (possibly null) URL comparator */
640       public FileComparator(Comparator JavaDoc urlComparator)
641       {
642          this.urlComparator = urlComparator;
643       }
644       
645       /**
646        * Compare all non-nulls as less than nulls. Next, compare as URL's
647        * using the delegated comparator. And finally, use File's natural
648        * ordering.
649        */

650       public int compare(Object JavaDoc o1, Object JavaDoc o2)
651       {
652          // catch nulls
653
if (o1 == o2)
654          {
655             return 0;
656          }
657          if (o1 == null)
658          {
659             return 1;
660          }
661          if (o2 == null)
662          {
663             return -1;
664          }
665          
666          // obtain the File and URL objects pertaining to each argument
667
File JavaDoc file1;
668          File JavaDoc file2;
669          URL JavaDoc url1;
670          URL JavaDoc url2;
671          
672          if (o1 instanceof URL JavaDoc)
673          {
674             url1 = (URL JavaDoc)o1;
675             file1 = new File JavaDoc(url1.getPath());
676          } else
677          {
678             file1 = (File JavaDoc)o1;
679             try
680             {
681                url1 = file1.toURL();
682             } catch (MalformedURLException JavaDoc e)
683             {
684                throw new IllegalStateException JavaDoc("Unable to create file url: " +
685                file1 + ": " + e);
686             }
687          }
688          if (o2 instanceof URL JavaDoc)
689          {
690             url2 = (URL JavaDoc)o2;
691             file2 = new File JavaDoc(url2.getPath());
692          } else
693          {
694             file2 = (File JavaDoc)o2;
695             try
696             {
697                url2 = file2.toURL();
698             } catch (MalformedURLException JavaDoc e)
699             {
700                throw new IllegalStateException JavaDoc("Unable to create file url: " +
701                file2 + ": " + e);
702             }
703          }
704          
705          // first, use the delegate URL comparator
706
int comp = 0;
707          if (urlComparator != null)
708          {
709             comp = urlComparator.compare(url1, url2);
710          }
711          
712          // If equal, break ties with File's natural ordering
713
if (comp == 0)
714          {
715             comp = file1.compareTo(file2);
716          }
717          
718          // normalize the comp value to -1, 0, 1
719
return comp < 0 ? -1 : comp > 0 ? 1 : 0;
720       }
721    }
722    
723    /**
724     * Scan all urls.
725     */

726    public void scan()
727    {
728       log.trace("Scanning urls...");
729       
730       // just scan all the scanners
731
for (Iterator JavaDoc iter = scanners.iterator(); iter.hasNext(); )
732       {
733          ((Scanner)iter.next()).scan();
734       }
735    }
736    
737    /**
738     * Obtain the Service values. This was copied from Jason Dillons
739     * URLDeploymentScanner. Thanks Jason!
740     */

741    public ObjectName JavaDoc preRegister(MBeanServer JavaDoc server, ObjectName JavaDoc name)
742       throws Exception JavaDoc
743    {
744       
745       // get server's home for relative paths, need this for setting
746
// attribute final values, so we need todo it here
747
serverHome = ServerConfigLocator.locate().getServerHomeDir();
748       
749       return super.preRegister(server, name);
750    }
751 }
752
Popular Tags