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