2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.intellij
.facet
.impl
.ui
.libraries
;
19 import com
.intellij
.facet
.ui
.libraries
.LibraryDownloadInfo
;
20 import com
.intellij
.facet
.ui
.libraries
.LibraryInfo
;
21 import com
.intellij
.ide
.IdeBundle
;
22 import com
.intellij
.openapi
.application
.PathManager
;
23 import com
.intellij
.openapi
.application
.Result
;
24 import com
.intellij
.openapi
.application
.WriteAction
;
25 import com
.intellij
.openapi
.fileChooser
.FileChooser
;
26 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptor
;
27 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptorFactory
;
28 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
29 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
30 import com
.intellij
.openapi
.progress
.ProgressManager
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.openapi
.ui
.Messages
;
33 import com
.intellij
.openapi
.util
.Pair
;
34 import com
.intellij
.openapi
.util
.Ref
;
35 import com
.intellij
.openapi
.util
.io
.FileUtil
;
36 import com
.intellij
.openapi
.util
.text
.StringUtil
;
37 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
38 import com
.intellij
.openapi
.vfs
.VfsUtil
;
39 import com
.intellij
.openapi
.vfs
.VirtualFile
;
40 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
41 import com
.intellij
.util
.io
.UrlConnectionUtil
;
42 import com
.intellij
.util
.net
.HttpConfigurable
;
43 import com
.intellij
.util
.net
.IOExceptionDialog
;
44 import org
.jetbrains
.annotations
.NonNls
;
45 import org
.jetbrains
.annotations
.NotNull
;
46 import org
.jetbrains
.annotations
.Nullable
;
50 import java
.net
.HttpURLConnection
;
52 import java
.util
.ArrayList
;
53 import java
.util
.List
;
58 public class LibraryDownloader
{
59 private static final int CONNECTION_TIMEOUT
= 60*1000;
60 private static final int READ_TIMEOUT
= 60*1000;
61 @NonNls private static final String LIB_SCHEMA
= "lib://";
63 private LibraryDownloadInfo
[] myLibraryInfos
;
64 private final LibraryDownloadingMirrorsMap myMirrorsMap
;
65 private JComponent myParent
;
66 private @Nullable Project myProject
;
67 private String myDirectoryForDownloadedLibrariesPath
;
68 private final @Nullable String myLibraryPresentableName
;
70 public LibraryDownloader(final LibraryDownloadInfo
[] libraryInfos
, final @Nullable Project project
, JComponent parent
, @Nullable String directoryForDownloadedLibrariesPath
,
71 @Nullable String libraryPresentableName
,
72 final LibraryDownloadingMirrorsMap mirrorsMap
) {
74 myLibraryInfos
= libraryInfos
;
76 myDirectoryForDownloadedLibrariesPath
= directoryForDownloadedLibrariesPath
;
77 myLibraryPresentableName
= libraryPresentableName
;
78 myMirrorsMap
= mirrorsMap
;
81 public LibraryDownloader(final LibraryDownloadInfo
[] libraryInfos
, final @Nullable Project project
, @NotNull JComponent parent
) {
82 this(libraryInfos
, project
, parent
, null, null, new LibraryDownloadingMirrorsMap());
85 public LibraryDownloader(final LibraryDownloadInfo
[] libraryInfos
, final @NotNull Project project
) {
86 myLibraryInfos
= libraryInfos
;
88 myLibraryPresentableName
= null;
89 myMirrorsMap
= new LibraryDownloadingMirrorsMap();
92 public VirtualFile
[] download() {
93 VirtualFile dir
= null;
94 if (myDirectoryForDownloadedLibrariesPath
!= null) {
95 File ioDir
= new File(FileUtil
.toSystemDependentName(myDirectoryForDownloadedLibrariesPath
));
97 dir
= LocalFileSystem
.getInstance().refreshAndFindFileByPath(myDirectoryForDownloadedLibrariesPath
);
101 dir
= chooseDirectoryForLibraries();
105 return doDownload(dir
);
107 return VirtualFile
.EMPTY_ARRAY
;
110 private VirtualFile
[] doDownload(final VirtualFile dir
) {
111 HttpConfigurable
.getInstance().setAuthenticator();
112 final List
<Pair
<LibraryDownloadInfo
, File
>> downloadedFiles
= new ArrayList
<Pair
<LibraryDownloadInfo
, File
>>();
113 final List
<VirtualFile
> existingFiles
= new ArrayList
<VirtualFile
>();
114 final Ref
<Exception
> exceptionRef
= Ref
.create(null);
115 final Ref
<LibraryDownloadInfo
> currentLibrary
= new Ref
<LibraryDownloadInfo
>();
117 String dialogTitle
= IdeBundle
.message("progress.download.libraries.title");
118 if (myLibraryPresentableName
!= null) {
119 dialogTitle
= IdeBundle
.message("progress.download.0.libraries.title", StringUtil
.capitalize(myLibraryPresentableName
));
122 ProgressManager
.getInstance().runProcessWithProgressSynchronously(new Runnable() {
124 final ProgressIndicator indicator
= ProgressManager
.getInstance().getProgressIndicator();
126 for (int i
= 0; i
< myLibraryInfos
.length
; i
++) {
127 LibraryDownloadInfo info
= myLibraryInfos
[i
];
128 currentLibrary
.set(info
);
129 if (indicator
!= null) {
130 indicator
.checkCanceled();
131 indicator
.setText(IdeBundle
.message("progress.0.of.1.file.downloaded.text", i
, myLibraryInfos
.length
));
134 final VirtualFile existing
= dir
.findChild(getExpectedFileName(info
));
135 long size
= existing
!= null ? existing
.getLength() : -1;
137 if (!download(info
, size
, downloadedFiles
)) {
138 existingFiles
.add(existing
);
142 catch (ProcessCanceledException e
) {
145 catch (IOException e
) {
149 }, dialogTitle
, true, myProject
, myParent
);
151 Exception exception
= exceptionRef
.get();
152 if (exception
== null) {
154 return moveToDir(existingFiles
, downloadedFiles
, dir
);
156 catch (IOException e
) {
157 final String title
= IdeBundle
.message("progress.download.libraries.title");
158 if (myProject
!= null) {
159 Messages
.showErrorDialog(myProject
, title
, e
.getMessage());
162 Messages
.showErrorDialog(myParent
, title
, e
.getMessage());
164 return VirtualFile
.EMPTY_ARRAY
;
168 deleteFiles(downloadedFiles
);
169 if (exception
instanceof IOException
) {
170 String message
= IdeBundle
.message("error.library.download.failed", exception
.getMessage());
171 if (currentLibrary
.get() != null) {
172 message
+= ": " + myMirrorsMap
.getDownloadingUrl(currentLibrary
.get());
174 final boolean tryAgain
= IOExceptionDialog
.showErrorDialog(IdeBundle
.message("progress.download.libraries.title"), message
);
176 return doDownload(dir
);
179 return VirtualFile
.EMPTY_ARRAY
;
182 private @Nullable VirtualFile
chooseDirectoryForLibraries() {
183 final FileChooserDescriptor descriptor
= FileChooserDescriptorFactory
.createSingleFolderDescriptor();
184 descriptor
.setTitle(IdeBundle
.message("dialog.directory.for.libraries.title"));
186 final VirtualFile
[] files
;
187 if (myProject
!= null) {
188 files
= FileChooser
.chooseFiles(myProject
, descriptor
, myProject
.getBaseDir());
191 files
= FileChooser
.chooseFiles(myParent
, descriptor
);
194 return files
.length
> 0 ? files
[0] : null;
197 private static VirtualFile
[] moveToDir(final List
<VirtualFile
> existingFiles
, final List
<Pair
<LibraryDownloadInfo
, File
>> downloadedFiles
, final VirtualFile dir
) throws IOException
{
198 List
<VirtualFile
> files
= new ArrayList
<VirtualFile
>();
200 final File ioDir
= VfsUtil
.virtualToIoFile(dir
);
201 for (Pair
<LibraryDownloadInfo
, File
> pair
: downloadedFiles
) {
202 final LibraryDownloadInfo info
= pair
.getFirst();
203 final boolean dontTouch
= info
.getDownloadUrl().startsWith(LIB_SCHEMA
) || info
.getDownloadUrl().startsWith(LocalFileSystem
.PROTOCOL_PREFIX
);
204 final File toFile
= dontTouch? pair
.getSecond() : generateName(info
, ioDir
);
206 FileUtil
.rename(pair
.getSecond(), toFile
);
208 VirtualFile file
= new WriteAction
<VirtualFile
>() {
209 protected void run(final Result
<VirtualFile
> result
) {
210 final String url
= VfsUtil
.getUrlForLibraryRoot(toFile
);
211 LocalFileSystem
.getInstance().refreshAndFindFileByIoFile(toFile
);
212 result
.setResult(VirtualFileManager
.getInstance().refreshAndFindFileByUrl(url
));
214 }.execute().getResultObject();
220 for (final VirtualFile file
: existingFiles
) {
221 VirtualFile libraryRootFile
= new WriteAction
<VirtualFile
>() {
222 protected void run(final Result
<VirtualFile
> result
) {
223 final String url
= VfsUtil
.getUrlForLibraryRoot(VfsUtil
.virtualToIoFile(file
));
224 result
.setResult(VirtualFileManager
.getInstance().refreshAndFindFileByUrl(url
));
227 }.execute().getResultObject();
228 if (libraryRootFile
!= null) {
229 files
.add(libraryRootFile
);
233 return files
.toArray(new VirtualFile
[files
.size()]);
236 private static String
getExpectedFileName(LibraryDownloadInfo info
) {
237 return info
.getFileNamePrefix() + info
.getFileNameSuffix();
240 private static File
generateName(LibraryDownloadInfo info
, final File dir
) {
241 String prefix
= info
.getFileNamePrefix();
242 String suffix
= info
.getFileNameSuffix();
243 File file
= new File(dir
, prefix
+ suffix
);
245 while (file
.exists()) {
246 file
= new File(dir
, prefix
+ "_" + count
+ suffix
);
252 private static void deleteFiles(final List
<Pair
<LibraryDownloadInfo
, File
>> pairs
) {
253 for (Pair
<LibraryDownloadInfo
, File
> pair
: pairs
) {
254 FileUtil
.delete(pair
.getSecond());
259 private boolean download(final LibraryDownloadInfo libraryInfo
, final long existingFileSize
, final List
<Pair
<LibraryDownloadInfo
, File
>> downloadedFiles
) throws IOException
{
260 final ProgressIndicator indicator
= ProgressManager
.getInstance().getProgressIndicator();
261 final String presentableUrl
= myMirrorsMap
.getPresentableUrl(libraryInfo
);
262 final String url
= myMirrorsMap
.getDownloadingUrl(libraryInfo
);
263 if (url
.startsWith(LIB_SCHEMA
)) {
264 indicator
.setText2(IdeBundle
.message("progress.locate.jar.text", getExpectedFileName(libraryInfo
)));
265 final String path
= url
.substring(LIB_SCHEMA
.length()).replace('/', File
.separatorChar
);
266 final File file
= PathManager
.findFileInLibDirectory(path
);
267 downloadedFiles
.add(Pair
.create(libraryInfo
, file
));
268 } else if (url
.startsWith(LocalFileSystem
.PROTOCOL_PREFIX
)) {
269 String path
= url
.substring(LocalFileSystem
.PROTOCOL_PREFIX
.length()).replace('/', File
.separatorChar
).replace('\\', File
.separatorChar
);
270 File file
= new File(path
);
271 if (file
.exists()) downloadedFiles
.add(Pair
.create(libraryInfo
, file
));
273 indicator
.setText2(IdeBundle
.message("progress.connecting.to.dowload.jar.text", presentableUrl
));
274 indicator
.setIndeterminate(true);
275 HttpURLConnection connection
= (HttpURLConnection
)new URL(url
).openConnection();
276 connection
.setConnectTimeout(CONNECTION_TIMEOUT
);
277 connection
.setReadTimeout(READ_TIMEOUT
);
279 InputStream input
= null;
280 BufferedOutputStream output
= null;
282 boolean deleteFile
= true;
283 File tempFile
= null;
285 final int responseCode
= connection
.getResponseCode();
286 if (responseCode
!= HttpURLConnection
.HTTP_OK
) {
287 throw new IOException(IdeBundle
.message("error.connection.failed.with.http.code.N", responseCode
));
290 final int size
= connection
.getContentLength();
291 if (size
!= -1 && size
== existingFileSize
) {
295 tempFile
= FileUtil
.createTempFile("downloaded", "jar");
296 input
= UrlConnectionUtil
.getConnectionInputStreamWithException(connection
, indicator
);
297 output
= new BufferedOutputStream(new FileOutputStream(tempFile
));
298 indicator
.setText2(IdeBundle
.message("progress.download.jar.text", getExpectedFileName(libraryInfo
), presentableUrl
));
299 indicator
.setIndeterminate(size
== -1);
302 final byte[] buf
= new byte[1024];
304 while ((len
= input
.read(buf
)) > 0) {
305 indicator
.checkCanceled();
308 indicator
.setFraction((double)count
/ size
);
310 output
.write(buf
, 0, len
);
314 downloadedFiles
.add(Pair
.create(libraryInfo
, tempFile
));
320 if (output
!= null) {
323 if (deleteFile
&& tempFile
!= null) {
324 FileUtil
.delete(tempFile
);
326 connection
.disconnect();
332 public static LibraryDownloadInfo
[] getDownloadingInfos(final LibraryInfo
[] libraries
) {
333 List
<LibraryDownloadInfo
> downloadInfos
= new ArrayList
<LibraryDownloadInfo
>();
334 for (LibraryInfo library
: libraries
) {
335 LibraryDownloadInfo downloadInfo
= library
.getDownloadingInfo();
336 if (downloadInfo
!= null) {
337 downloadInfos
.add(downloadInfo
);
340 return downloadInfos
.toArray(new LibraryDownloadInfo
[downloadInfos
.size()]);