View Javadoc

1   package com.atlassian.plugin.loaders;
2   
3   import org.apache.commons.logging.Log;
4   import org.apache.commons.logging.LogFactory;
5   import org.apache.commons.lang.Validate;
6   
7   import java.io.File;
8   import java.io.FilenameFilter;
9   import java.util.*;
10  
11  import com.atlassian.plugin.loaders.classloading.*;
12  import com.atlassian.plugin.PluginException;
13  
14  /**
15   * Scans the filesystem for changed or added plugin files and stores a map of the currently known ones.
16   *
17   * @since 2.1.0
18   */
19  class DirectoryScanner implements com.atlassian.plugin.loaders.classloading.Scanner
20  {
21      private static Log log = LogFactory.getLog(DirectoryScanner.class);
22  
23      /**
24       * Tracks the classloading
25       */
26      private final File pluginsDirectory;
27  
28      /**
29       * A Map of {@link String} absolute file paths to {@link DeploymentUnit}s.
30       */
31      private final Map<String,DeploymentUnit> scannedDeploymentUnits = new TreeMap<String,DeploymentUnit>();
32  
33  
34      /**
35       * Constructor for scanner.
36       *
37       * @param pluginsDirectory the directory that the scanner should monitor for plugins
38       */
39      public DirectoryScanner(File pluginsDirectory)
40      {
41          Validate.notNull(pluginsDirectory, "Plugin scanner directory must not be null");
42          this.pluginsDirectory = pluginsDirectory;
43      }
44  
45      private DeploymentUnit createAndStoreDeploymentUnit(File file)
46      {
47          if (isScanned(file))
48              return null;
49  
50          DeploymentUnit unit = new DeploymentUnit(file);
51          scannedDeploymentUnits.put(file.getAbsolutePath(), unit);
52  
53          return unit;
54      }
55  
56      /**
57       * Given a file, finds the deployment unit for it if one has already been scanned.
58       * @param file a jar file.
59       * @return the stored deploymentUnit matching the file or null if none exists.
60       */
61      public DeploymentUnit locateDeploymentUnit(File file)
62      {
63          return scannedDeploymentUnits.get(file.getAbsolutePath());
64      }
65  
66      /**
67       * Finds whether the given file has been scanned already.
68       */
69      private boolean isScanned(File file)
70      {
71          return locateDeploymentUnit(file) != null;
72      }
73  
74      /**
75       * Tells the Scanner to forget about a file it has loaded so that it will reload it
76       * next time it scans.
77       *
78       * @param file a file that may have already been scanned.
79       */
80      public void clear(File file)
81      {
82          scannedDeploymentUnits.remove(file.getAbsolutePath());
83      }
84  
85      /**
86       * Scans for jars that have been added or modified since the last call to scan.
87       *
88       * @return Collection of {@link DeploymentUnit}s that describe newly added Jars.
89       */
90      public Collection<DeploymentUnit> scan()
91      {
92          // Checks to see if we have deleted any of the deployment units.
93          List<File> removedFiles = new ArrayList<File>();
94          for (DeploymentUnit unit : scannedDeploymentUnits.values())
95          {
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 that don't start in '.'
104         Collection<DeploymentUnit> result = new ArrayList<DeploymentUnit>();
105         File files[] = pluginsDirectory.listFiles(new FilenameFilter()
106         {
107 
108             public boolean accept(File dir, String name)
109             {
110                 return !name.startsWith(".");
111             }
112         });
113 
114         if (files == null)
115         {
116             log.error("listFiles returned null for directory " + pluginsDirectory.getAbsolutePath());
117             return result;
118         }
119 
120         Arrays.sort(files); // sorts by filename for deterministic load order
121         for (File file : files)
122         {
123             if (isScanned(file) && isModified(file))
124             {
125                 clear(file);
126                 DeploymentUnit unit = createAndStoreDeploymentUnit(file);
127                 if (unit != null)
128                     result.add(unit);
129             }
130             else if (!isScanned(file))
131             {
132                 DeploymentUnit unit = createAndStoreDeploymentUnit(file);
133                 if (unit != null)
134                     result.add(unit);
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<File> toUndeploy)
147     {
148         for (File aToUndeploy : toUndeploy)
149         {
150             clear( aToUndeploy);
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<DeploymentUnit> getDeploymentUnits()
160     {
161         return Collections.unmodifiableCollection(scannedDeploymentUnits.values());
162     }
163 
164     /**
165      * Clears the list of scanned deployment units.
166      */
167     public void reset()
168     {
169         scannedDeploymentUnits.clear();
170     }
171 
172     public void remove(DeploymentUnit unit) throws PluginException
173     {
174         if (!unit.getPath().delete())
175             throw new PluginException("Unable to delete file: " + unit.getPath());
176 
177         clear(unit.getPath());
178     }
179 }