1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.atlassian.theplugin.idea.autoupdate;
18
19 import com.atlassian.theplugin.commons.configuration.GeneralConfigurationBean;
20 import com.atlassian.theplugin.commons.util.LoggerImpl;
21 import com.atlassian.theplugin.idea.IdeaActionScheduler;
22 import com.atlassian.theplugin.util.InfoServer;
23 import com.atlassian.theplugin.util.PluginUtil;
24 import com.intellij.ide.plugins.IdeaPluginDescriptor;
25 import com.intellij.ide.plugins.PluginManager;
26 import com.intellij.ide.startup.StartupActionScriptManager;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.application.PathManager;
29 import com.intellij.openapi.extensions.PluginId;
30 import com.intellij.openapi.ui.DialogWrapper;
31 import com.intellij.openapi.ui.Messages;
32 import com.intellij.openapi.util.io.FileUtil;
33 import com.intellij.openapi.util.io.StreamUtil;
34 import com.intellij.util.io.ZipUtil;
35 import org.jetbrains.annotations.NotNull;
36
37 import java.io.*;
38 import java.net.HttpURLConnection;
39 import java.net.URL;
40 import java.net.URLConnection;
41 import java.net.URLEncoder;
42
43
44
45
46
47
48
49
50
51 public class PluginDownloader {
52
53
54 public static final String PLUGIN_ID_TOKEN = "PLUGIN_ID";
55 public static final String VERSION_TOKEN = "BUILD";
56
57 private static String pluginName = PluginUtil.getInstance().getName();
58
59 private static final int TIMEOUT = 15000;
60 private static final int EXTENTION_LENGHT = 3;
61 private InfoServer.VersionInfo newVersion;
62 private GeneralConfigurationBean updateConfiguration;
63
64 public void setTimeout(int timeout) {
65 this.timeout = timeout;
66 }
67
68 public void setReadTimeout(int readTimeout) {
69 this.readTimeout = readTimeout;
70 }
71
72 private int timeout = TIMEOUT;
73 private int readTimeout = TIMEOUT;
74
75 public PluginDownloader(InfoServer.VersionInfo newVersion, GeneralConfigurationBean updateConfiguration) {
76 this.newVersion = newVersion;
77 this.updateConfiguration = updateConfiguration;
78 }
79
80 public void run() {
81 try {
82 final File tmpDownloadFile = downloadPluginFromServer(
83 this.newVersion.getDownloadUrl(), new File(PathManager.getPluginsPath()));
84
85
86
87
88
89
90 IdeaPluginDescriptor pluginDescr = PluginManager.getPlugin(PluginId.getId(PluginUtil.getInstance().getPluginId()));
91
92
93
94 if (pluginDescr == null) {
95 pluginDescr = PluginManager.getPlugin(PluginId.getId(PluginUtil.getInstance().getName()));
96 }
97
98 if (pluginDescr == null) {
99 IdeaActionScheduler.getInstance().invokeLater(new Runnable() {
100 public void run() {
101
102 Messages.showErrorDialog("Cannot retrieve plugin descriptor", "Error installing plugin");
103 }
104 });
105 return;
106 }
107 addActions(pluginDescr, tmpDownloadFile);
108
109
110 promptShutdownAndShutdown();
111
112 } catch (final IOException e) {
113 PluginUtil.getLogger().warn(e.getMessage(), e);
114 IdeaActionScheduler.getInstance().invokeLater(new Runnable() {
115 public void run() {
116 Messages.showErrorDialog(e.getMessage(), "Error downloading and installing plugin");
117 }
118 });
119
120 }
121 }
122
123 private void promptShutdownAndShutdown() {
124 ApplicationManager.getApplication().invokeLater(new Runnable() {
125 public void run() {
126 String title = "IDEA shutdown";
127 String message =
128 "Atlassian IntelliJ Connector has been installed successfully.\n"
129 + "IntelliJ IDEA needs to be restarted to activate the plugin.\n"
130 + "Would you like to shutdown IntelliJ IDEA now?";
131
132 int answer = Messages.showYesNoDialog(
133 message, title, Messages.getQuestionIcon());
134 if (answer == DialogWrapper.OK_EXIT_CODE) {
135
136 ApplicationManager.getApplication().exit();
137 }
138 }
139 });
140 }
141
142
143 File downloadPluginFromServer(String version, @NotNull final File destinationDir) throws IOException {
144 File pluginArchiveFile = FileUtil.createTempFile("temp_" + pluginName + "_", "tmp");
145
146 String pluginUrl = newVersion.getDownloadUrl()
147 .replaceAll(PLUGIN_ID_TOKEN, URLEncoder.encode(pluginName, "UTF-8"))
148 .replaceAll(VERSION_TOKEN, URLEncoder.encode(version, "UTF-8"));
149 if (!pluginUrl.contains("?")) {
150 pluginUrl += "?";
151 }
152 pluginUrl += "uid=" + URLEncoder.encode(Long.toString(updateConfiguration.getUid()), "UTF-8");
153
154 PluginUtil.getLogger().info("Downloading plugin archive from: " + pluginUrl);
155
156
157 URL url = new URL(pluginUrl);
158 URLConnection connection = url.openConnection();
159 connection.setConnectTimeout(getTimeout());
160 connection.setReadTimeout(getReadTimeout());
161 connection.connect();
162
163 InputStream inputStream = null;
164 OutputStream outputStream = null;
165 try {
166 inputStream = connection.getInputStream();
167 outputStream = new FileOutputStream(pluginArchiveFile);
168 StreamUtil.copyStreamContent(inputStream, outputStream);
169 } catch (FileNotFoundException e) {
170 PluginUtil.getLogger().warn("File not found " + pluginArchiveFile.getPath(), e);
171 throw e;
172 } catch (IOException e) {
173 PluginUtil.getLogger().warn(e);
174 throw e;
175 } finally {
176
177 if (inputStream != null) {
178 try {
179 inputStream.close();
180 } catch (IOException ioe) {
181 PluginUtil.getLogger().warn("Exception while closing input stream");
182
183 }
184 }
185
186 if (outputStream != null) {
187 try {
188 outputStream.close();
189 } catch (IOException ioe) {
190 PluginUtil.getLogger().warn("Exception while closing output stream");
191
192 }
193 }
194
195 if (connection instanceof HttpURLConnection) {
196 ((HttpURLConnection) connection).disconnect();
197 PluginUtil.getLogger().info("Disconnecting HttpURLConnection");
198 }
199 }
200 PluginUtil.getLogger().info("Downloaded file has [" + pluginArchiveFile.length() + "] bytes");
201 String srcName = connection.getURL().toString();
202 String ext = srcName.substring(srcName.lastIndexOf("."));
203 if (ext.contains("?")) {
204 ext = ext.substring(0, ext.indexOf("?"));
205 }
206 String newName = pluginArchiveFile.getName().substring(0, pluginArchiveFile.getName().length()
207 - EXTENTION_LENGHT) + ext;
208 File newFile = new File(destinationDir, newName);
209 PluginUtil.getLogger().info("Renaming downloaded file from [" + pluginArchiveFile.getAbsolutePath()
210 + "] to [" + newFile + "]");
211
212 if (!pluginArchiveFile.renameTo(newFile)) {
213 try {
214 FileUtil.copy(pluginArchiveFile, newFile);
215 } catch (IOException e) {
216 throw new IOException("Renaming file from [" + pluginArchiveFile.getAbsolutePath() + "] to ["
217 + newFile.getAbsolutePath() + "] failed.");
218 } finally {
219 if (!pluginArchiveFile.delete()) {
220 LoggerImpl.getInstance().warn("Deleting file [" + pluginArchiveFile.getAbsolutePath() + "] failed.");
221 }
222 }
223 }
224
225 PluginUtil.getLogger().info("After renaming file has [" + newFile.length() + "] bytes");
226 return newFile;
227 }
228
229 private void addActions(@NotNull final IdeaPluginDescriptor installedPlugin, File localArchiveFile) throws IOException {
230 PluginUtil.getLogger().info("IdeaPluginDescriptor [" + installedPlugin.getPluginId() + "]");
231
232 PluginId id = installedPlugin.getPluginId();
233
234 if (PluginManager.isPluginInstalled(id)) {
235
236 File oldFile = installedPlugin.getPath();
237 StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(oldFile);
238 StartupActionScriptManager.addActionCommand(deleteOld);
239 PluginUtil.getLogger().info("Queueing deletion of [" + oldFile.getPath() + "], exists [" + oldFile.exists() + "]");
240 } else {
241
242 PluginUtil.getLogger().warn("Install error. Cannot find plugin [" + installedPlugin.getName()
243 + "] with id [" + id.getIdString() + "]. Cannot delete old plugin version installing new version");
244 }
245
246
247 boolean isJarFile = localArchiveFile.getName().endsWith(".jar")
248 || localArchiveFile.getName().contains(".jar?");
249
250 if (isJarFile) {
251
252 String fileName = localArchiveFile.getName();
253 File newFile = new File(PathManager.getPluginsPath() + File.separator + fileName);
254 StartupActionScriptManager.ActionCommand copyPlugin =
255 new StartupActionScriptManager.CopyCommand(localArchiveFile, newFile);
256 StartupActionScriptManager.addActionCommand(copyPlugin);
257 PluginUtil.getLogger().info("Queueing copying of jar [" + localArchiveFile.getAbsolutePath() + "] to ["
258 + newFile.getAbsolutePath() + "]");
259 } else {
260
261 String unzipPath;
262 if (ZipUtil.isZipContainsFolder(localArchiveFile)) {
263 unzipPath = PathManager.getPluginsPath();
264 PluginUtil.getLogger().info("Zip [" + localArchiveFile + "] contains a root folder");
265 } else {
266 String dirName = installedPlugin.getName();
267 unzipPath = PathManager.getPluginsPath() + File.separator + dirName;
268 PluginUtil.getLogger().info("Zip [" + localArchiveFile + "] does not contain a root folder");
269 }
270
271 File newFile = new File(unzipPath);
272 StartupActionScriptManager.ActionCommand unzip =
273 new StartupActionScriptManager.UnzipCommand(localArchiveFile, newFile);
274 StartupActionScriptManager.addActionCommand(unzip);
275 PluginUtil.getLogger().info("Queueing unzipping/copying of [" + localArchiveFile.getAbsolutePath() + "] to ["
276 + newFile.getAbsolutePath() + "]");
277 }
278
279
280 StartupActionScriptManager.ActionCommand deleteTemp =
281 new StartupActionScriptManager.DeleteCommand(localArchiveFile);
282 StartupActionScriptManager.addActionCommand(deleteTemp);
283 PluginUtil.getLogger().info("Queueing deletion of [" + localArchiveFile.getAbsolutePath() + "]");
284 }
285
286 public int getTimeout() {
287 return timeout;
288 }
289
290 public int getReadTimeout() {
291 return readTimeout;
292 }
293 }