1 package com.atlassian.plugin.loaders;
2
3 import com.atlassian.plugin.PluginException;
4 import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
5 import com.atlassian.plugin.loaders.classloading.Scanner;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 import java.io.File;
10 import java.io.FileNotFoundException;
11 import java.io.IOException;
12 import java.nio.file.AccessDeniedException;
13 import java.nio.file.Files;
14 import java.nio.file.NoSuchFileException;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.TreeMap;
22
23 import static com.google.common.base.Preconditions.checkNotNull;
24
25
26
27
28
29
30
31 public class DirectoryScanner implements Scanner {
32
33 private static Logger log = LoggerFactory.getLogger(DirectoryScanner.class);
34
35
36
37
38 private final File pluginsDirectory;
39
40
41
42
43 private final Map<String, DeploymentUnit> scannedDeploymentUnits = new TreeMap<>();
44
45
46
47
48
49
50 public DirectoryScanner(final File pluginsDirectory) {
51 this.pluginsDirectory = checkNotNull(pluginsDirectory);
52 }
53
54 private DeploymentUnit createAndStoreDeploymentUnit(final File file) {
55 if (isScanned(file))
56 return null;
57
58 final DeploymentUnit unit = new DeploymentUnit(file);
59 scannedDeploymentUnits.put(file.getAbsolutePath(), unit);
60
61 return unit;
62 }
63
64
65
66
67
68
69
70 public DeploymentUnit locateDeploymentUnit(final File file) {
71 return scannedDeploymentUnits.get(file.getAbsolutePath());
72 }
73
74
75
76
77 private boolean isScanned(final File file) {
78 return locateDeploymentUnit(file) != null;
79 }
80
81
82
83
84
85
86
87 public void clear(final File file) {
88 scannedDeploymentUnits.remove(file.getAbsolutePath());
89 }
90
91
92
93
94
95
96
97
98 public Collection<DeploymentUnit> scan() {
99
100 final List<File> removedFiles = new ArrayList<>();
101 for (final DeploymentUnit unit : scannedDeploymentUnits.values()) {
102 if (!unit.getPath().exists() || !unit.getPath().canRead()) {
103 removedFiles.add(unit.getPath());
104 }
105 }
106 clear(removedFiles);
107
108
109 final Collection<DeploymentUnit> result = new ArrayList<>();
110 final File files[] = pluginsDirectory.listFiles((dir, name) -> !name.startsWith("."));
111
112 if (files == null) {
113 log.error("listFiles returned null for directory " + pluginsDirectory.getAbsolutePath());
114 return result;
115 }
116
117 Arrays.sort(files);
118 for (final File file : files) {
119 if (isScanned(file) && isModified(file)) {
120 clear(file);
121 final DeploymentUnit unit = createAndStoreDeploymentUnit(file);
122 if (unit != null)
123 result.add(unit);
124 } else if (!isScanned(file)) {
125 final DeploymentUnit unit = createAndStoreDeploymentUnit(file);
126 if (unit != null)
127 result.add(unit);
128 }
129 }
130 return result;
131 }
132
133 private boolean isModified(final File file) {
134 final DeploymentUnit unit = locateDeploymentUnit(file);
135 return file.lastModified() > unit.lastModified();
136 }
137
138 private void clear(final List<File> toUndeploy) {
139 for (final File aToUndeploy : toUndeploy) {
140 clear(aToUndeploy);
141 }
142 }
143
144
145
146
147
148
149 public Collection<DeploymentUnit> getDeploymentUnits() {
150 return Collections.unmodifiableCollection(scannedDeploymentUnits.values());
151 }
152
153
154
155
156 public void reset() {
157 scannedDeploymentUnits.clear();
158 }
159
160 public void remove(final DeploymentUnit unit) throws PluginException {
161 final File file = unit.getPath();
162 try {
163
164
165
166
167
168
169
170 Files.delete(file.toPath());
171 } catch (final AccessDeniedException e) {
172 log.info("Plugin file <{}> exists but we do not have permission to remove it. Ignoring.",
173 file.getAbsolutePath());
174 } catch (final FileNotFoundException | NoSuchFileException e) {
175 log.debug("Plugin file <{}> doesn't exist to delete. Ignoring.", file.getAbsolutePath());
176 } catch (final IOException e) {
177 throw new PluginException("Unable to delete plugin file: " + file.getAbsolutePath());
178 }
179
180 clear(file);
181 }
182 }