View Javadoc

1   package com.atlassian.plugin.loaders.classloading;
2   
3   import org.apache.commons.logging.Log;
4   import org.apache.commons.logging.LogFactory;
5   
6   import java.io.File;
7   import java.io.FileFilter;
8   import java.net.MalformedURLException;
9   import java.util.*;
10  
11  /**
12   * Scans the filesystem for changed or added plugin jars and stores a map of the currently known ones.
13   */
14  public class Scanner
15  {
16      private static Log log = LogFactory.getLog(Scanner.class);
17  
18      /**
19       * File filter used to load just the jars.
20       */
21      private final static FileFilter fileFilter = new JarFileFilter();
22  
23      /**
24       * Tracks the classloading
25       */
26      private File libDir;
27  
28      /**
29       * A Map of {@link String} absolute file paths to {@link DeploymentUnit}s.
30       */
31      private Map scannedDeploymentUnits = new HashMap();
32  
33  
34      /**
35       * Constructor for scanner.
36       *
37       * @param libDir
38       */
39      public Scanner(File libDir)
40      {
41          this.libDir = libDir;
42      }
43  
44      private DeploymentUnit createAndStoreDeploymentUnit(File file) throws MalformedURLException
45      {
46          if (isScanned(file))
47              return null;
48  
49          DeploymentUnit unit = new DeploymentUnit(file);
50          scannedDeploymentUnits.put(file.getAbsolutePath(), unit);
51  
52          return unit;
53      }
54  
55      /**
56       * Given a file, finds the deployment unit for it if one has already been scanned.
57       * @param file a jar file.
58       * @return the stored deploymentUnit matching the file or null if none exists.
59       */
60      public DeploymentUnit locateDeploymentUnit(File file)
61      {
62          return (DeploymentUnit) scannedDeploymentUnits.get(file.getAbsolutePath());
63      }
64  
65      /**
66       * Finds whether the given file has been scanned already.
67       */
68      private boolean isScanned(File file)
69      {
70          return locateDeploymentUnit(file) != null;
71      }
72  
73      /**
74       * Tells the Scanner to forget about a file it has loaded so that it will reload it
75       * next time it scans.
76       *
77       * @param file a file that may have already been scanned.
78       */
79      public void clear(File file)
80      {
81          scannedDeploymentUnits.remove(file.getAbsolutePath());
82      }
83  
84      /**
85       * Scans for jars that have been added or modified since the last call to scan.
86       *
87       * @return Collection of {@link DeploymentUnit}s that describe newly added Jars.
88       */
89      public Collection scan()
90      {
91          // Checks to see if we have deleted any of the deployment units.
92          List removedFiles = new ArrayList();
93          for (Iterator iterator = scannedDeploymentUnits.values().iterator(); iterator.hasNext();)
94          {
95              DeploymentUnit unit = (DeploymentUnit) iterator.next();
96              if (!unit.getPath().exists() || !unit.getPath().canRead())
97              {
98                  removedFiles.add(unit.getPath());
99              }
100         }
101         clear(removedFiles);
102 
103         // Checks for new files.
104         Collection result = new ArrayList();
105         File files[] = libDir.listFiles(fileFilter);
106         if (files == null)
107         {
108             log.error("listFiles returned null for directory " + libDir.getAbsolutePath());
109         }
110         else
111         {
112             for (int i = 0; i < files.length; i++)
113             {
114                 File file = files[i];
115                 try
116                 {
117                     if (isScanned(file) && isModified(file))
118                     {
119                         clear(file);
120                         DeploymentUnit unit = createAndStoreDeploymentUnit(file);
121                         if (unit != null)
122                             result.add(unit);
123                     }
124                     else if (!isScanned(file))
125                     {
126                         DeploymentUnit unit = createAndStoreDeploymentUnit(file);
127                         if (unit != null)
128                             result.add(unit);                        
129                     }
130                 }
131                 catch (MalformedURLException e)
132                 {
133                     log.error("Error deploying plugin " + file.getAbsolutePath(), e);
134                 }
135             }
136         }
137         return result;
138     }
139 
140     private boolean isModified(File file)
141     {
142         DeploymentUnit unit = locateDeploymentUnit(file);
143         return file.lastModified() > unit.lastModified();
144     }
145 
146     private void clear(List toUndeploy)
147     {
148         for (Iterator iterator = toUndeploy.iterator(); iterator.hasNext();)
149         {
150             clear((File) iterator.next());
151         }
152     }
153 
154     /**
155      * Retrieve all the {@link DeploymentUnit}s currently stored.
156      *
157      * @return the complete unmodifiable list of scanned {@link DeploymentUnit}s.
158      */
159     public Collection getDeploymentUnits()
160     {
161         return Collections.unmodifiableCollection(scannedDeploymentUnits.values());
162     }
163 
164     /**
165      * Clears the list of scanned deployment units.
166      */
167     public void clearAll()
168     {
169         scannedDeploymentUnits.clear();
170     }
171 
172     /**
173      * Reinvents the wheel and lets only files ending in .jar pass through.
174      */
175     static class JarFileFilter implements FileFilter
176     {
177         public boolean accept(File file)
178         {
179             return file.getName().endsWith(".jar");
180         }
181     }
182 }