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 files 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       * Tracks the classloading
20       */
21      private File libDir;
22  
23      /**
24       * A Map of {@link String} absolute file paths to {@link DeploymentUnit}s.
25       */
26      private Map<String,DeploymentUnit> scannedDeploymentUnits = new HashMap<String,DeploymentUnit>();
27  
28  
29      /**
30       * Constructor for scanner.
31       *
32       * @param libDir
33       */
34      public Scanner(File libDir)
35      {
36          this.libDir = libDir;
37      }
38  
39      private DeploymentUnit createAndStoreDeploymentUnit(File file) throws MalformedURLException
40      {
41          if (isScanned(file))
42              return null;
43  
44          DeploymentUnit unit = new DeploymentUnit(file);
45          scannedDeploymentUnits.put(file.getAbsolutePath(), unit);
46  
47          return unit;
48      }
49  
50      /**
51       * Given a file, finds the deployment unit for it if one has already been scanned.
52       * @param file a jar file.
53       * @return the stored deploymentUnit matching the file or null if none exists.
54       */
55      public DeploymentUnit locateDeploymentUnit(File file)
56      {
57          return scannedDeploymentUnits.get(file.getAbsolutePath());
58      }
59  
60      /**
61       * Finds whether the given file has been scanned already.
62       */
63      private boolean isScanned(File file)
64      {
65          return locateDeploymentUnit(file) != null;
66      }
67  
68      /**
69       * Tells the Scanner to forget about a file it has loaded so that it will reload it
70       * next time it scans.
71       *
72       * @param file a file that may have already been scanned.
73       */
74      public void clear(File file)
75      {
76          scannedDeploymentUnits.remove(file.getAbsolutePath());
77      }
78  
79      /**
80       * Scans for jars that have been added or modified since the last call to scan.
81       *
82       * @return Collection of {@link DeploymentUnit}s that describe newly added Jars.
83       */
84      public Collection<DeploymentUnit> scan()
85      {
86          // Checks to see if we have deleted any of the deployment units.
87          List<File> removedFiles = new ArrayList<File>();
88          for (DeploymentUnit unit : scannedDeploymentUnits.values())
89          {
90              if (!unit.getPath().exists() || !unit.getPath().canRead())
91              {
92                  removedFiles.add(unit.getPath());
93              }
94          }
95          clear(removedFiles);
96  
97          // Checks for new files.
98          Collection<DeploymentUnit> result = new ArrayList<DeploymentUnit>();
99          File files[] = libDir.listFiles();
100         if (files == null)
101         {
102             log.error("listFiles returned null for directory " + libDir.getAbsolutePath());
103         }
104         else
105         {
106             for (File file : files)
107             {
108                 try
109                 {
110                     if (isScanned(file) && isModified(file))
111                     {
112                         clear(file);
113                         DeploymentUnit unit = createAndStoreDeploymentUnit(file);
114                         if (unit != null)
115                             result.add(unit);
116                     } else if (!isScanned(file))
117                     {
118                         DeploymentUnit unit = createAndStoreDeploymentUnit(file);
119                         if (unit != null)
120                             result.add(unit);
121                     }
122                 }
123                 catch (MalformedURLException e)
124                 {
125                     log.error("Error deploying plugin " + file.getAbsolutePath(), e);
126                 }
127             }
128         }
129         return result;
130     }
131 
132     private boolean isModified(File file)
133     {
134         DeploymentUnit unit = locateDeploymentUnit(file);
135         return file.lastModified() > unit.lastModified();
136     }
137 
138     private void clear(List<File> toUndeploy)
139     {
140         for (File aToUndeploy : toUndeploy)
141         {
142             clear( aToUndeploy);
143         }
144     }
145 
146     /**
147      * Retrieve all the {@link DeploymentUnit}s currently stored.
148      *
149      * @return the complete unmodifiable list of scanned {@link DeploymentUnit}s.
150      */
151     public Collection<DeploymentUnit> getDeploymentUnits()
152     {
153         return Collections.unmodifiableCollection(scannedDeploymentUnits.values());
154     }
155 
156     /**
157      * Clears the list of scanned deployment units.
158      */
159     public void clearAll()
160     {
161         scannedDeploymentUnits.clear();
162     }
163 }