View Javadoc

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