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.util.*;
9   
10  import com.atlassian.plugin.loaders.classloading.*;
11  import com.atlassian.plugin.PluginException;
12  
13  /**
14   * Scans the filesystem for changed or added plugin files and stores a map of the currently known ones.
15   *
16   * @since 2.1.0
17   */
18  class DirectoryScanner implements com.atlassian.plugin.loaders.classloading.Scanner
19  {
20      private static Log log = LogFactory.getLog(DirectoryScanner.class);
21  
22      /**
23       * Tracks the classloading
24       */
25      private final File pluginsDirectory;
26  
27      /**
28       * A Map of {@link String} absolute file paths to {@link DeploymentUnit}s.
29       */
30      private final Map<String,DeploymentUnit> scannedDeploymentUnits = new TreeMap<String,DeploymentUnit>();
31  
32  
33      /**
34       * Constructor for scanner.
35       *
36       * @param pluginsDirectory the directory that the scanner should monitor for plugins
37       */
38      public DirectoryScanner(File pluginsDirectory)
39      {
40          Validate.notNull(pluginsDirectory, "Plugin scanner directory must not be null");
41          this.pluginsDirectory = pluginsDirectory;
42      }
43  
44      private DeploymentUnit createAndStoreDeploymentUnit(File file)
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 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<DeploymentUnit> scan()
90      {
91          // Checks to see if we have deleted any of the deployment units.
92          List<File> removedFiles = new ArrayList<File>();
93          for (DeploymentUnit unit : scannedDeploymentUnits.values())
94          {
95              if (!unit.getPath().exists() || !unit.getPath().canRead())
96              {
97                  removedFiles.add(unit.getPath());
98              }
99          }
100         clear(removedFiles);
101 
102         // Checks for new files.
103         Collection<DeploymentUnit> result = new ArrayList<DeploymentUnit>();
104         File files[] = pluginsDirectory.listFiles();
105         if (files == null)
106         {
107             log.error("listFiles returned null for directory " + pluginsDirectory.getAbsolutePath());
108             return result;
109         }
110 
111         Arrays.sort(files); // sorts by filename for deterministic load order
112         for (File file : files)
113         {
114             if (isScanned(file) && isModified(file))
115             {
116                 clear(file);
117                 DeploymentUnit unit = createAndStoreDeploymentUnit(file);
118                 if (unit != null)
119                     result.add(unit);
120             }
121             else if (!isScanned(file))
122             {
123                 DeploymentUnit unit = createAndStoreDeploymentUnit(file);
124                 if (unit != null)
125                     result.add(unit);
126             }
127         }
128         return result;
129     }
130 
131     private boolean isModified(File file)
132     {
133         DeploymentUnit unit = locateDeploymentUnit(file);
134         return file.lastModified() > unit.lastModified();
135     }
136 
137     private void clear(List<File> toUndeploy)
138     {
139         for (File aToUndeploy : toUndeploy)
140         {
141             clear( aToUndeploy);
142         }
143     }
144 
145     /**
146      * Retrieve all the {@link DeploymentUnit}s currently stored.
147      *
148      * @return the complete unmodifiable list of scanned {@link DeploymentUnit}s.
149      */
150     public Collection<DeploymentUnit> getDeploymentUnits()
151     {
152         return Collections.unmodifiableCollection(scannedDeploymentUnits.values());
153     }
154 
155     /**
156      * Clears the list of scanned deployment units.
157      */
158     public void reset()
159     {
160         scannedDeploymentUnits.clear();
161     }
162 
163     public void remove(DeploymentUnit unit) throws PluginException
164     {
165         if (!unit.getPath().delete())
166             throw new PluginException("Unable to delete file: " + unit.getPath());
167 
168         clear(unit.getPath());
169     }
170 }