1 package com.atlassian.plugin.repositories;
2
3 import com.atlassian.plugin.PluginArtifact;
4 import com.atlassian.plugin.RevertablePluginInstaller;
5 import com.atlassian.util.concurrent.CopyOnWriteMap;
6 import org.apache.commons.io.FileUtils;
7 import org.apache.commons.io.IOUtils;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10
11 import java.io.File;
12 import java.io.FileOutputStream;
13 import java.io.FilenameFilter;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.io.OutputStream;
17 import java.util.Map;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.base.Preconditions.checkState;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class FilePluginInstaller implements RevertablePluginInstaller
39 {
40 private static final Logger log = LoggerFactory.getLogger(FilePluginInstaller.class);
41
42 public static final String ORIGINAL_PREFIX = ".original-";
43
44 private final File directory;
45 private final Map<String, BackupRepresentation> installedPlugins = CopyOnWriteMap.<String, BackupRepresentation>builder().stableViews().newHashMap();
46
47
48
49
50 public FilePluginInstaller(File directory)
51 {
52 this.directory = checkNotNull(directory);
53 checkState(directory.exists(), "The plugin installation directory must exist, %s", directory.getAbsolutePath());
54 }
55
56
57
58
59
60
61 public void installPlugin(String key, PluginArtifact pluginArtifact)
62 {
63 checkNotNull(key, "The plugin key must be specified");
64 checkNotNull(pluginArtifact, "The plugin artifact must not be null");
65
66 final File newPluginFile = new File(directory, pluginArtifact.getName());
67 try
68 {
69 backup(key, newPluginFile);
70 if (newPluginFile.exists())
71 {
72
73 newPluginFile.delete();
74 }
75 }
76 catch (IOException e)
77 {
78 log.warn("Unable to backup old file", e);
79 }
80
81 OutputStream os = null;
82 InputStream in = null;
83 try
84 {
85 os = new FileOutputStream(newPluginFile);
86 in = pluginArtifact.getInputStream();
87 IOUtils.copy(in, os);
88 }
89 catch (IOException e)
90 {
91 throw new RuntimeException("Could not install plugin: " + pluginArtifact, e);
92 }
93 finally
94 {
95 IOUtils.closeQuietly(in);
96 IOUtils.closeQuietly(os);
97 }
98 }
99
100
101
102
103
104
105
106 public void revertInstalledPlugin(String pluginKey)
107 {
108 BackupRepresentation backup = installedPlugins.get(pluginKey);
109 if (backup != null)
110 {
111 File currentFile = new File(backup.getBackupFile().getParent(), backup.getCurrentPluginFilename());
112 if (currentFile.exists())
113 {
114 currentFile.delete();
115 }
116
117
118
119 if (backup.isUpgrade())
120 {
121 try
122 {
123 FileUtils.moveFile(backup.getBackupFile(), new File(backup.getBackupFile().getParent(), backup.getOriginalPluginArtifactFilename()));
124 }
125 catch (IOException e)
126 {
127 log.warn("Unable to restore old plugin for " + pluginKey);
128 }
129 }
130 }
131 }
132
133
134
135
136
137
138 public void clearBackups()
139 {
140 for (File file : directory.listFiles(new BackupNameFilter()))
141 {
142 file.delete();
143 }
144 installedPlugins.clear();
145 }
146
147 private void backup(String pluginKey, File currentPluginArtifact) throws IOException
148 {
149 BackupRepresentation orig = null;
150
151
152 if (!installedPlugins.containsKey(pluginKey))
153 {
154 orig = getBackupRepresentation(pluginKey, currentPluginArtifact);
155 }
156
157
158 else
159 {
160 final BackupRepresentation oldBackupFile = installedPlugins.get(pluginKey);
161
162
163 orig = new BackupRepresentation(oldBackupFile, currentPluginArtifact.getName());
164
165
166 final File previousPluginFile = new File(oldBackupFile.getBackupFile().getParent(), oldBackupFile.getCurrentPluginFilename());
167 if (previousPluginFile.exists())
168 {
169 previousPluginFile.delete();
170 }
171 }
172
173
174 installedPlugins.put(pluginKey, orig);
175 }
176
177 private BackupRepresentation getBackupRepresentation(final String pluginKey, final File currentPluginArtifact) throws IOException
178 {
179
180
181 if (currentPluginArtifact.exists())
182 {
183 File backupFile = new File(currentPluginArtifact.getParent(), ORIGINAL_PREFIX + currentPluginArtifact.getName());
184 if (backupFile.exists())
185 {
186 throw new IOException("Existing backup found for plugin " + pluginKey + ". Cannot install.");
187 }
188
189 FileUtils.copyFile(currentPluginArtifact, backupFile);
190 return new BackupRepresentation(backupFile, currentPluginArtifact.getName());
191 }
192
193 else
194 {
195 return new BackupRepresentation(currentPluginArtifact, currentPluginArtifact.getName());
196 }
197 }
198
199 private static class BackupNameFilter implements FilenameFilter
200 {
201 public boolean accept(File dir, String name)
202 {
203 return name.startsWith(ORIGINAL_PREFIX);
204 }
205 }
206
207 private static class BackupRepresentation
208 {
209 private final File backupFile;
210 private final String originalPluginArtifactFilename;
211 private final String currentPluginFilename;
212 private final boolean isUpgrade;
213
214
215
216
217
218 public BackupRepresentation(File backupFile, String originalPluginArtifactFilename)
219 {
220 this.backupFile = checkNotNull(backupFile, "backupFile");
221 this.originalPluginArtifactFilename = checkNotNull(originalPluginArtifactFilename, "originalPluginArtifactFilename");
222 this.isUpgrade = !backupFile.getName().equals(originalPluginArtifactFilename);
223 this.currentPluginFilename = originalPluginArtifactFilename;
224 }
225
226
227
228
229
230 public BackupRepresentation(BackupRepresentation oldBackup, String currentPluginFilename)
231 {
232 this.backupFile = checkNotNull(oldBackup, "oldBackup").backupFile;
233 this.originalPluginArtifactFilename = oldBackup.originalPluginArtifactFilename;
234 this.isUpgrade = oldBackup.isUpgrade;
235 this.currentPluginFilename = checkNotNull(currentPluginFilename, "currentPluginFilename");
236 }
237
238 public File getBackupFile()
239 {
240 return backupFile;
241 }
242
243 public String getOriginalPluginArtifactFilename()
244 {
245 return originalPluginArtifactFilename;
246 }
247
248 public String getCurrentPluginFilename()
249 {
250 return currentPluginFilename;
251 }
252
253 public boolean isUpgrade()
254 {
255 return isUpgrade;
256 }
257 }
258 }