View Javadoc

1   /**
2    * Copyright (C) 2008 Atlassian
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.atlassian.theplugin.idea.autoupdate;
18  
19  import com.atlassian.theplugin.commons.configuration.GeneralConfigurationBean;
20  import com.atlassian.theplugin.util.InfoServer;
21  import com.atlassian.theplugin.util.PluginUtil;
22  import com.intellij.ide.plugins.IdeaPluginDescriptor;
23  import com.intellij.ide.plugins.PluginManager;
24  import com.intellij.ide.startup.StartupActionScriptManager;
25  import com.intellij.openapi.application.ApplicationManager;
26  import com.intellij.openapi.application.PathManager;
27  import com.intellij.openapi.extensions.PluginId;
28  import com.intellij.openapi.ui.DialogWrapper;
29  import com.intellij.openapi.ui.Messages;
30  import com.intellij.openapi.util.io.FileUtil;
31  import com.intellij.openapi.util.io.StreamUtil;
32  import com.intellij.util.io.ZipUtil;
33  
34  import java.io.*;
35  import java.net.HttpURLConnection;
36  import java.net.URL;
37  import java.net.URLConnection;
38  import java.net.URLEncoder;
39  
40  /**
41   * Created by IntelliJ IDEA.
42   * User: Jacek
43   * Date: 2008-02-20
44   * Time: 11:37:09
45   * To change this template use File | Settings | File Templates.
46   */
47  
48  public class PluginDownloader { //implements Runnable {
49  
50  
51  	public static final String PLUGIN_ID_TOKEN = "PLUGIN_ID";
52  	public static final String VERSION_TOKEN = "BUILD";
53  
54  	private static String pluginName = PluginUtil.getInstance().getName();
55  
56  	private static final int TIMEOUT = 15000;
57  	private static final int EXTENTION_LENGHT = 3;
58  	private InfoServer.VersionInfo newVersion;
59  	private GeneralConfigurationBean updateConfiguration;
60  
61  	public void setTimeout(int timeout) {
62  		this.timeout = timeout;
63  	}
64  
65  	public void setReadTimeout(int readTimeout) {
66  		this.readTimeout = readTimeout;
67  	}
68  
69  	private int timeout = TIMEOUT;
70  	private int readTimeout = TIMEOUT;
71  
72  	public PluginDownloader(InfoServer.VersionInfo newVersion, GeneralConfigurationBean updateConfiguration) {
73  		this.newVersion = newVersion;
74  		this.updateConfiguration = updateConfiguration;
75  	}
76  
77  	public void run() {
78  		try {
79  			File localArchiveFile = downloadPluginFromServer(this.newVersion.getDownloadUrl());
80  
81  			// add startup actions
82  
83  			// todo lguminski/jjaroczynski to find a better way of getting plugin descriptor
84  			// theoritically openapi should provide a method so the plugin could get info on itself
85  
86  			IdeaPluginDescriptor pluginDescr = PluginManager.getPlugin(PluginId.getId(PluginUtil.getInstance().getPluginId()));
87  			/* todo lguminsk when you debug the plugin it appears in registry as attlassian-idea-plugin, but when
88  			 	you rinstall it notmally it appears as Atlassian. Thats why it is double checked here
89  			    */
90  			if (pluginDescr == null) {
91  				pluginDescr = PluginManager.getPlugin(PluginId.getId(PluginUtil.getInstance().getName()));
92  			}
93  			addActions(pluginDescr, localArchiveFile);
94  
95  			// restart IDEA
96  			promptShutdownAndShutdown();
97  
98  		} catch (IOException e) {
99  			PluginUtil.getLogger().info("Error registering action in IDEA", e);
100 		}
101 	}
102 
103 	private void promptShutdownAndShutdown() {
104 		ApplicationManager.getApplication().invokeLater(new Runnable() {
105 			public void run() {
106 				String title = "IDEA shutdown";
107 				String message =
108 						"Plugin has been installed successfully. Do you want to restart IDEA to activate the plugin?";
109 
110 				int answer = Messages.showYesNoDialog(
111 						message, title, Messages.getQuestionIcon());
112 
113 //						Messages.showDialog(PluginUpdateNotifierBundle.message("progress.shutdown.dialog.text"),
114 //                        PluginUpdateNotifierBundle.message("progress.shutdown.dialog.title"),
115 //                        new String[]{PluginUpdateNotifierBundle.message("progress.shutdown.dialog.button.shutdown.text"),
116 //                                PluginUpdateNotifierBundle.message("progress.shutdown.dialog.button.cancel.text")},
117 //                        0, Messages.getQuestionIcon());
118 
119 				if (answer == DialogWrapper.OK_EXIT_CODE) {
120 					//ApplicationManagerEx.getApplicationEx().exit(true);
121 					ApplicationManager.getApplication().exit();
122 				}
123 			}
124 		});
125 	}
126 
127 	// todo add info about licence and author
128 	private File downloadPluginFromServer(String version) throws IOException {
129 		File pluginArchiveFile = FileUtil.createTempFile("temp_" + pluginName + "_", "tmp");
130 
131 
132 		String pluginUrl = null;
133 		pluginUrl = newVersion.getDownloadUrl()
134 				.replaceAll(PLUGIN_ID_TOKEN, URLEncoder.encode(pluginName, "UTF-8"))
135 				.replaceAll(VERSION_TOKEN, URLEncoder.encode(version, "UTF-8"));
136 		if (!pluginUrl.contains("?")) {
137 			pluginUrl += "?";
138 		}
139 		pluginUrl += "uid=" + URLEncoder.encode(Long.toString(updateConfiguration.getUid()), "UTF-8");
140 
141 		PluginUtil.getLogger().info("Downloading plugin archive from: " + pluginUrl);
142 
143 		//HttpConfigurable.getInstance().prepareURL(pluginUrl);
144 		URL url = new URL(pluginUrl);
145 		URLConnection connection = url.openConnection();
146 		connection.setConnectTimeout(getTimeout());
147 		connection.setReadTimeout(getReadTimeout());
148 		connection.connect();
149 
150 		InputStream inputStream = null;
151 		OutputStream outputStream = null;
152 		try {
153 			inputStream = connection.getInputStream();
154 			outputStream = new FileOutputStream(pluginArchiveFile);
155 			StreamUtil.copyStreamContent(inputStream, outputStream);
156 		} finally {
157 			if (inputStream != null) {
158 				try {
159 					inputStream.close();
160 				} catch (IOException ioe) {
161 					// nothing we can do at this point
162 				}
163 			}
164 
165 			if (outputStream != null) {
166 				try {
167 					outputStream.close();
168 				} catch (IOException ioe) {
169 					// nothing we can do at this point
170 				}
171 			}
172 
173 			if (connection instanceof HttpURLConnection) {
174 				((HttpURLConnection) connection).disconnect();
175 			}
176 		}
177 		String srcName = connection.getURL().toString();
178 		String ext = srcName.substring(srcName.lastIndexOf("."));
179 		if (ext.contains("?")) {
180 			ext = ext.substring(0, ext.indexOf("?"));
181 		}
182 		String newName = pluginArchiveFile.getPath().substring(0, pluginArchiveFile.getPath().length()
183 				- EXTENTION_LENGHT) + ext;
184 		File newFile = new File(newName);
185 		if (!pluginArchiveFile.renameTo(new File(newName))) {
186 			pluginArchiveFile.delete();
187 			throw new IOException("Renaming received file from \"" + srcName + "\" to \"" + newName + "\" failed.");
188 		}
189 		return newFile;
190 	}
191 
192 	private void addActions(IdeaPluginDescriptor installedPlugin, File localArchiveFile) throws IOException {
193 
194 		PluginId id = installedPlugin.getPluginId();
195 
196 		if (PluginManager.isPluginInstalled(id)) {
197 			// store old plugins file
198 			File oldFile = installedPlugin.getPath();
199 			StartupActionScriptManager.ActionCommand deleteOld = new StartupActionScriptManager.DeleteCommand(oldFile);
200 			StartupActionScriptManager.addActionCommand(deleteOld);
201 		}
202 
203 		//noinspection HardCodedStringLiteral
204 		boolean isJarFile = localArchiveFile.getName().endsWith(".jar")
205 				|| localArchiveFile.getName().contains(".jar?");
206 
207 		if (isJarFile) {
208 			// add command to copy file to the IDEA/plugins path
209 			String fileName = localArchiveFile.getName();
210 			File newFile = new File(PathManager.getPluginsPath() + File.separator + fileName);
211 			StartupActionScriptManager.ActionCommand copyPlugin =
212 					new StartupActionScriptManager.CopyCommand(localArchiveFile, newFile);
213 			StartupActionScriptManager.addActionCommand(copyPlugin);
214 		} else {
215 			// add command to unzip file to the IDEA/plugins path
216 			String unzipPath;
217 			if (ZipUtil.isZipContainsFolder(localArchiveFile)) {
218 				unzipPath = PathManager.getPluginsPath();
219 			} else {
220 				String dirName = installedPlugin.getName();
221 				unzipPath = PathManager.getPluginsPath() + File.separator + dirName;
222 			}
223 
224 			File newFile = new File(unzipPath);
225 			StartupActionScriptManager.ActionCommand unzip =
226 					new StartupActionScriptManager.UnzipCommand(localArchiveFile, newFile);
227 			StartupActionScriptManager.addActionCommand(unzip);
228 		}
229 
230 		// add command to remove temp plugin file
231 		StartupActionScriptManager.ActionCommand deleteTemp =
232 				new StartupActionScriptManager.DeleteCommand(localArchiveFile);
233 		StartupActionScriptManager.addActionCommand(deleteTemp);
234 	}
235 
236 	public int getTimeout() {
237 		return timeout;
238 	}
239 
240 	public int getReadTimeout() {
241 		return readTimeout;
242 	}
243 }