1 package com.atlassian.plugin.repositories;
2
3 import com.atlassian.plugin.PluginArtifact;
4 import com.atlassian.plugin.RevertablePluginInstaller;
5 import io.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 private static final Logger log = LoggerFactory.getLogger(FilePluginInstaller.class);
40
41 public static final String ORIGINAL_PREFIX = ".original-";
42
43 private final File directory;
44 private final Map<String, BackupRepresentation> installedPlugins = CopyOnWriteMap.<String, BackupRepresentation>builder().stableViews().newHashMap();
45
46
47
48
49 public FilePluginInstaller(File directory) {
50 this.directory = checkNotNull(directory);
51 checkState(directory.exists(), "The plugin installation directory must exist, %s", directory.getAbsolutePath());
52 }
53
54
55
56
57
58
59 public void installPlugin(String key, PluginArtifact pluginArtifact) {
60 checkNotNull(key, "The plugin key must be specified");
61 checkNotNull(pluginArtifact, "The plugin artifact must not be null");
62
63 final File newPluginFile = new File(directory, pluginArtifact.getName());
64 try {
65 backup(key, newPluginFile);
66 if (newPluginFile.exists()) {
67
68 newPluginFile.delete();
69 }
70 } catch (IOException e) {
71 log.warn("Unable to backup old file", e);
72 }
73
74 OutputStream os = null;
75 InputStream in = null;
76 try {
77 os = new FileOutputStream(newPluginFile);
78 in = pluginArtifact.getInputStream();
79 IOUtils.copy(in, os);
80 } catch (IOException e) {
81 throw new RuntimeException("Could not install plugin: " + pluginArtifact, e);
82 } finally {
83 IOUtils.closeQuietly(in);
84 IOUtils.closeQuietly(os);
85 }
86 }
87
88
89
90
91
92
93
94 public void revertInstalledPlugin(String pluginKey) {
95 BackupRepresentation backup = installedPlugins.get(pluginKey);
96 if (backup != null) {
97 File currentFile = new File(backup.getBackupFile().getParent(), backup.getCurrentPluginFilename());
98 if (currentFile.exists()) {
99 currentFile.delete();
100 }
101
102
103
104 if (backup.isUpgrade()) {
105 try {
106 FileUtils.moveFile(backup.getBackupFile(), new File(backup.getBackupFile().getParent(), backup.getOriginalPluginArtifactFilename()));
107 } catch (IOException e) {
108 log.warn("Unable to restore old plugin for " + pluginKey);
109 }
110 }
111 }
112 }
113
114
115
116
117
118
119 public void clearBackups() {
120 File[] files = directory.listFiles(new BackupNameFilter());
121 if (files != null) {
122 for (File file : files) {
123 file.delete();
124 }
125 }
126 installedPlugins.clear();
127 }
128
129 private void backup(String pluginKey, File currentPluginArtifact) throws IOException {
130 BackupRepresentation orig = null;
131
132
133 if (!installedPlugins.containsKey(pluginKey)) {
134 orig = getBackupRepresentation(pluginKey, currentPluginArtifact);
135 }
136
137
138 else {
139 final BackupRepresentation oldBackupFile = installedPlugins.get(pluginKey);
140
141
142 orig = new BackupRepresentation(oldBackupFile, currentPluginArtifact.getName());
143
144
145 final File previousPluginFile = new File(oldBackupFile.getBackupFile().getParent(), oldBackupFile.getCurrentPluginFilename());
146 if (previousPluginFile.exists()) {
147 previousPluginFile.delete();
148 }
149 }
150
151
152 installedPlugins.put(pluginKey, orig);
153 }
154
155 private BackupRepresentation getBackupRepresentation(final String pluginKey, final File currentPluginArtifact) throws IOException {
156
157
158 if (currentPluginArtifact.exists()) {
159 File backupFile = new File(currentPluginArtifact.getParent(), ORIGINAL_PREFIX + currentPluginArtifact.getName());
160 if (backupFile.exists()) {
161 throw new IOException("Existing backup found for plugin " + pluginKey + ". Cannot install.");
162 }
163
164 FileUtils.copyFile(currentPluginArtifact, backupFile);
165 return new BackupRepresentation(backupFile, currentPluginArtifact.getName());
166 }
167
168 else {
169 return new BackupRepresentation(currentPluginArtifact, currentPluginArtifact.getName());
170 }
171 }
172
173 private static class BackupNameFilter implements FilenameFilter {
174 public boolean accept(File dir, String name) {
175 return name.startsWith(ORIGINAL_PREFIX);
176 }
177 }
178
179 private static class BackupRepresentation {
180 private final File backupFile;
181 private final String originalPluginArtifactFilename;
182 private final String currentPluginFilename;
183 private final boolean isUpgrade;
184
185
186
187
188
189 public BackupRepresentation(File backupFile, String originalPluginArtifactFilename) {
190 this.backupFile = checkNotNull(backupFile, "backupFile");
191 this.originalPluginArtifactFilename = checkNotNull(originalPluginArtifactFilename, "originalPluginArtifactFilename");
192 this.isUpgrade = !backupFile.getName().equals(originalPluginArtifactFilename);
193 this.currentPluginFilename = originalPluginArtifactFilename;
194 }
195
196
197
198
199
200 public BackupRepresentation(BackupRepresentation oldBackup, String currentPluginFilename) {
201 this.backupFile = checkNotNull(oldBackup, "oldBackup").backupFile;
202 this.originalPluginArtifactFilename = oldBackup.originalPluginArtifactFilename;
203 this.isUpgrade = oldBackup.isUpgrade;
204 this.currentPluginFilename = checkNotNull(currentPluginFilename, "currentPluginFilename");
205 }
206
207 public File getBackupFile() {
208 return backupFile;
209 }
210
211 public String getOriginalPluginArtifactFilename() {
212 return originalPluginArtifactFilename;
213 }
214
215 public String getCurrentPluginFilename() {
216 return currentPluginFilename;
217 }
218
219 public boolean isUpgrade() {
220 return isUpgrade;
221 }
222 }
223 }