001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.plugin; 020 021import java.net.URL; 022import java.util.ArrayList; 023import java.util.EventListener; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Set; 027 028import javax.swing.event.EventListenerList; 029 030import icy.file.FileUtil; 031import icy.gui.dialog.ConfirmDialog; 032import icy.gui.frame.progress.AnnounceFrame; 033import icy.gui.frame.progress.CancelableProgressFrame; 034import icy.gui.frame.progress.DownloadFrame; 035import icy.gui.frame.progress.FailedAnnounceFrame; 036import icy.gui.frame.progress.SuccessfullAnnounceFrame; 037import icy.main.Icy; 038import icy.network.NetworkUtil; 039import icy.network.URLUtil; 040import icy.plugin.PluginDescriptor.PluginIdent; 041import icy.preferences.RepositoryPreferences.RepositoryInfo; 042import icy.system.IcyExceptionHandler; 043import icy.system.thread.ThreadUtil; 044import icy.update.Updater; 045import icy.util.StringUtil; 046import icy.util.XMLUtil; 047import icy.util.ZipUtil; 048 049/** 050 * @author Stephane 051 */ 052public class PluginInstaller implements Runnable 053{ 054 public static interface PluginInstallerListener extends EventListener 055 { 056 public void pluginInstalled(PluginDescriptor plugin, boolean success); 057 058 public void pluginRemoved(PluginDescriptor plugin, boolean success); 059 } 060 061 private static class PluginInstallInfo 062 { 063 // final PluginRepositoryLoader loader; 064 final PluginDescriptor plugin; 065 final boolean showProgress; 066 067 public PluginInstallInfo(PluginDescriptor plugin, boolean showProgress) 068 { 069 super(); 070 071 this.plugin = plugin; 072 this.showProgress = showProgress; 073 } 074 075 @Override 076 public boolean equals(Object obj) 077 { 078 if (obj instanceof PluginInstallInfo) 079 return ((PluginInstallInfo) obj).plugin.equals(plugin); 080 081 return super.equals(obj); 082 } 083 084 @Override 085 public int hashCode() 086 { 087 return plugin.hashCode(); 088 } 089 } 090 091 private static final String ERROR_DOWNLOAD = "Error while downloading "; 092 private static final String ERROR_SAVE = "Error while saving"; 093 // private static final String INSTALL_CANCELED = "Plugin installation canceled by user."; 094 095 /** 096 * static class 097 */ 098 private static final PluginInstaller instance = new PluginInstaller(); 099 100 /** 101 * plugin(s) to install FIFO 102 */ 103 private final List<PluginInstallInfo> installFIFO; 104 /** 105 * plugin(s) to delete FIFO 106 */ 107 private final List<PluginInstallInfo> removeFIFO; 108 109 /** 110 * listeners 111 */ 112 private final EventListenerList listeners; 113 114 /** 115 * internals 116 */ 117 private final List<PluginDescriptor> installingPlugins; 118 private final List<PluginDescriptor> desinstallingPlugin; 119 120 /** 121 * static class 122 */ 123 private PluginInstaller() 124 { 125 super(); 126 127 installFIFO = new ArrayList<PluginInstallInfo>(); 128 removeFIFO = new ArrayList<PluginInstallInfo>(); 129 130 listeners = new EventListenerList(); 131 132 installingPlugins = new ArrayList<PluginDescriptor>(); 133 desinstallingPlugin = new ArrayList<PluginDescriptor>(); 134 135 // launch installer thread 136 new Thread(this, "Plugin installer").start(); 137 } 138 139 /** 140 * Return true if install or desinstall is possible 141 */ 142 private static boolean isEnabled() 143 { 144 return !PluginLoader.isJCLDisabled(); 145 } 146 147 /** 148 * Install a plugin (asynchronous) 149 * 150 * @param plugin 151 * the plugin to install 152 * @param showProgress 153 * show a progress frame during process 154 */ 155 public static void install(PluginDescriptor plugin, boolean showProgress) 156 { 157 if ((plugin != null) && isEnabled()) 158 { 159 if (!NetworkUtil.hasInternetAccess()) 160 { 161 final String text = "Cannot install '" + plugin.getName() 162 + "' plugin : you are not connected to Internet."; 163 164 if (Icy.getMainInterface().isHeadLess()) 165 System.err.println(text); 166 else 167 new FailedAnnounceFrame(text, 10); 168 169 return; 170 } 171 172 synchronized (instance.installFIFO) 173 { 174 instance.installFIFO.add(new PluginInstallInfo(plugin, showProgress)); 175 } 176 } 177 } 178 179 /** 180 * return true if PluginInstaller is processing 181 */ 182 public static boolean isProcessing() 183 { 184 return isInstalling() || isDesinstalling(); 185 } 186 187 /** 188 * return a copy of the install FIFO 189 */ 190 public static ArrayList<PluginInstallInfo> getInstallFIFO() 191 { 192 synchronized (instance.installFIFO) 193 { 194 return new ArrayList<PluginInstaller.PluginInstallInfo>(instance.installFIFO); 195 } 196 } 197 198 /** 199 * Wait while installer is installing plugin. 200 */ 201 public static void waitInstall() 202 { 203 while (isInstalling()) 204 ThreadUtil.sleep(100); 205 } 206 207 /** 208 * return true if PluginInstaller is installing plugin(s) 209 */ 210 public static boolean isInstalling() 211 { 212 return !instance.installFIFO.isEmpty() || !instance.installingPlugins.isEmpty(); 213 } 214 215 /** 216 * return true if 'plugin' is in the install FIFO 217 */ 218 public static boolean isWaitingForInstall(PluginDescriptor plugin) 219 { 220 synchronized (instance.installFIFO) 221 { 222 for (PluginInstallInfo info : instance.installFIFO) 223 if (plugin == info.plugin) 224 return true; 225 } 226 227 return false; 228 } 229 230 /** 231 * return true if specified plugin is currently being installed or will be installed 232 */ 233 public static boolean isInstallingPlugin(PluginDescriptor plugin) 234 { 235 return (instance.installingPlugins.indexOf(plugin) != -1) || isWaitingForInstall(plugin); 236 } 237 238 /** 239 * Uninstall a plugin (asynchronous) 240 * 241 * @param plugin 242 * the plugin to uninstall 243 * @param showConfirm 244 * show a confirmation dialog 245 * @param showProgress 246 * show a progress frame during process 247 */ 248 public static void desinstall(PluginDescriptor plugin, boolean showConfirm, boolean showProgress) 249 { 250 if ((plugin != null) && isEnabled()) 251 { 252 if (showConfirm) 253 { 254 // get local plugins which depend from the plugin we want to delete 255 final List<PluginDescriptor> dependants = getLocalDependenciesFrom(plugin); 256 257 String message = "<html>"; 258 259 if (!dependants.isEmpty()) 260 { 261 message = message + "The following plugin(s) won't work anymore :<br>"; 262 263 for (PluginDescriptor depPlug : dependants) 264 message = message + depPlug.getName() + " " + depPlug.getVersion() + "<br>"; 265 266 message = message + "<br>"; 267 } 268 269 message = message + "Are you sure you want to remove '" + plugin.getName() + " " + plugin.getVersion() 270 + "' ?</html>"; 271 272 if (ConfirmDialog.confirm(message)) 273 { 274 synchronized (instance.removeFIFO) 275 { 276 instance.removeFIFO.add(new PluginInstallInfo(plugin, showConfirm)); 277 } 278 } 279 } 280 else 281 { 282 synchronized (instance.removeFIFO) 283 { 284 instance.removeFIFO.add(new PluginInstallInfo(plugin, showProgress)); 285 } 286 } 287 } 288 } 289 290 /** 291 * @deprecated Use {@link #desinstall(PluginDescriptor, boolean, boolean)} instead. 292 */ 293 @Deprecated 294 public static void desinstall(PluginDescriptor plugin, boolean showConfirm) 295 { 296 desinstall(plugin, showConfirm, showConfirm); 297 } 298 299 /** 300 * return a copy of the remove FIFO 301 */ 302 public static ArrayList<PluginInstallInfo> getRemoveFIFO() 303 { 304 synchronized (instance.removeFIFO) 305 { 306 return new ArrayList<PluginInstaller.PluginInstallInfo>(instance.removeFIFO); 307 } 308 } 309 310 /** 311 * Wait while installer is removing plugin. 312 */ 313 public static void waitDesinstall() 314 { 315 while (isDesinstalling()) 316 ThreadUtil.sleep(100); 317 } 318 319 /** 320 * return true if PluginInstaller is desinstalling plugin(s) 321 */ 322 public static boolean isDesinstalling() 323 { 324 return !instance.removeFIFO.isEmpty() || !instance.desinstallingPlugin.isEmpty(); 325 } 326 327 /** 328 * return true if 'plugin' is in the remove FIFO 329 */ 330 public static boolean isWaitingForDesinstall(PluginDescriptor plugin) 331 { 332 synchronized (instance.removeFIFO) 333 { 334 for (PluginInstallInfo info : instance.removeFIFO) 335 if (plugin == info.plugin) 336 return true; 337 } 338 339 return false; 340 } 341 342 /** 343 * return true if specified plugin is currently being desinstalled or will be desinstalled 344 */ 345 public static boolean isDesinstallingPlugin(PluginDescriptor plugin) 346 { 347 return (instance.desinstallingPlugin.indexOf(plugin) != -1) || isWaitingForDesinstall(plugin); 348 } 349 350 @Override 351 public void run() 352 { 353 while (!Thread.interrupted()) 354 { 355 // process installations 356 if (!installFIFO.isEmpty()) 357 { 358 // so list has sometime to fill-up 359 ThreadUtil.sleep(200); 360 361 do 362 installInternal(); 363 while (!installFIFO.isEmpty()); 364 } 365 366 // process deletions 367 while (!removeFIFO.isEmpty()) 368 { 369 // so list has sometime to fill-up 370 ThreadUtil.sleep(200); 371 372 do 373 desinstallInternal(); 374 while (!removeFIFO.isEmpty()); 375 } 376 377 ThreadUtil.sleep(100); 378 } 379 } 380 381 /** 382 * Backup specified plugin if it already exists.<br> 383 * Return an empty string if no error else return error message 384 */ 385 private static String backup(PluginDescriptor plugin) 386 { 387 boolean ok; 388 389 // backup JAR, XML and image files 390 ok = Updater.backup(plugin.getJarFilename()) && Updater.backup(plugin.getXMLFilename()) 391 && Updater.backup(plugin.getIconFilename()) && Updater.backup(plugin.getImageFilename()); 392 393 if (!ok) 394 return "Can't backup plugin '" + plugin.getName() + "'"; 395 396 return ""; 397 } 398 399 /** 400 * Return an empty string if no error else return error message 401 */ 402 private static String downloadAndSavePlugin(PluginDescriptor plugin, DownloadFrame taskFrame) 403 { 404 String result; 405 406 if (taskFrame != null) 407 taskFrame.setMessage("Downloading " + plugin); 408 409 // ensure descriptor is loaded 410 plugin.loadDescriptor(); 411 412 final RepositoryInfo repos = plugin.getRepository(); 413 final String login; 414 final String pass; 415 416 // use authentication (repos should not be null at this point) 417 if (repos.isAuthenticationEnabled()) 418 { 419 login = repos.getLogin(); 420 pass = repos.getPassword(); 421 } 422 else 423 { 424 login = null; 425 pass = null; 426 } 427 428 // try to build the final path using base repository address and plugin relative address 429 // (useful for local repository) 430 URL url; 431 final String basePath = FileUtil.getDirectory(repos.getLocation()); 432 // download and save JAR file 433 url = URLUtil.buildURL(basePath, plugin.getJarUrl()); 434 result = downloadAndSave(url, plugin.getJarFilename(), login, pass, true, taskFrame); 435 if (!StringUtil.isEmpty(result)) 436 return result; 437 438 // verify JAR file is not corrupted 439 if (!ZipUtil.isValid(plugin.getJarFilename(), false)) 440 return "Downloaded JAR file '" + plugin.getJarFilename() + "' is corrupted !"; 441 442 // download and save XML file 443 url = URLUtil.buildURL(basePath, plugin.getUrl()); 444 result = downloadAndSave(url, plugin.getXMLFilename(), login, pass, true, taskFrame); 445 if (!StringUtil.isEmpty(result)) 446 return result; 447 448 // verify XML file is not corrupted 449 if (XMLUtil.loadDocument(plugin.getXMLFilename()) == null) 450 return "Downloaded XML file '" + plugin.getXMLFilename() + "' is corrupted !"; 451 452 // download and save icon & image files 453 if (!StringUtil.isEmpty(plugin.getIconUrl())) 454 { 455 url = URLUtil.buildURL(basePath, plugin.getIconUrl()); 456 downloadAndSave(url, plugin.getIconFilename(), login, pass, false, taskFrame); 457 } 458 if (!StringUtil.isEmpty(plugin.getImageUrl())) 459 { 460 url = URLUtil.buildURL(basePath, plugin.getImageUrl()); 461 downloadAndSave(url, plugin.getImageFilename(), login, pass, false, taskFrame); 462 } 463 464 return ""; 465 } 466 467 /** 468 * Return an empty string if no error else return error message 469 */ 470 private static String downloadAndSave(URL downloadPath, String savePath, String login, String pass, 471 boolean displayError, DownloadFrame downloadFrame) 472 { 473 if (downloadFrame != null) 474 downloadFrame.setPath(FileUtil.getFileName(savePath)); 475 476 // load data 477 final byte[] data = NetworkUtil.download(downloadPath, login, pass, downloadFrame, displayError); 478 if (data == null) 479 return ERROR_DOWNLOAD + downloadPath.toString(); 480 481 // save data 482 if (!FileUtil.save(savePath, data, displayError)) 483 { 484 System.err.println("Can't write '" + savePath + "' !"); 485 System.err.println("File may be locked or you don't own the rights to write files here."); 486 return ERROR_SAVE + savePath; 487 } 488 489 return null; 490 } 491 492 private static boolean deletePlugin(PluginDescriptor plugin) 493 { 494 if (!FileUtil.delete(plugin.getJarFilename(), false)) 495 { 496 System.err.println("Can't delete '" + plugin.getJarFilename() + "' file !"); 497 // fatal error 498 return false; 499 } 500 501 if (FileUtil.exists(plugin.getXMLFilename())) 502 if (!FileUtil.delete(plugin.getXMLFilename(), false)) 503 System.err.println("Can't delete '" + plugin.getXMLFilename() + "' file !"); 504 505 FileUtil.delete(plugin.getImageFilename(), false); 506 FileUtil.delete(plugin.getIconFilename(), false); 507 508 return true; 509 } 510 511 /** 512 * Fill list with local dependencies (plugins) of specified plugin 513 */ 514 public static void getLocalDependenciesOf(List<PluginDescriptor> result, PluginDescriptor plugin) 515 { 516 // load plugin descriptor informations if not yet done 517 plugin.loadDescriptor(); 518 519 for (PluginIdent ident : plugin.getRequired()) 520 { 521 // already in our dependences ? --> pass to the next one 522 if (PluginDescriptor.getPlugin(result, ident, true) != null) 523 continue; 524 525 // find local dependent plugin 526 final PluginDescriptor dep = PluginLoader.getPlugin(ident, true); 527 528 // dependence found ? 529 if (dep != null) 530 { 531 // and add it to list 532 PluginDescriptor.addToList(result, dep); 533 // search its dependencies too 534 getLocalDependenciesOf(result, dep); 535 } 536 } 537 } 538 539 /** 540 * Return local plugins list which depend from the specified list of plugins. 541 */ 542 public static List<PluginDescriptor> getLocalDependenciesFrom(List<PluginDescriptor> plugins) 543 { 544 final List<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 545 546 for (PluginDescriptor plugin : plugins) 547 getLocalDependenciesFrom(plugin, result); 548 549 return result; 550 } 551 552 /** 553 * Return local plugins list which depend from the specified plugin. 554 */ 555 public static List<PluginDescriptor> getLocalDependenciesFrom(PluginDescriptor plugin) 556 { 557 final List<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 558 559 getLocalDependenciesFrom(plugin, result); 560 561 return result; 562 } 563 564 /** 565 * Return local plugins list which depend from the specified plugin. 566 */ 567 private static void getLocalDependenciesFrom(PluginDescriptor plugin, List<PluginDescriptor> result) 568 { 569 for (PluginDescriptor curPlug : PluginLoader.getPlugins(false)) 570 // require specified plugin ? 571 if (curPlug.requires(plugin)) 572 PluginDescriptor.addToList(result, curPlug); 573 } 574 575 /** 576 * Fill list with 'sources' dependencies of specified plugin 577 */ 578 private static void getLocalDependenciesOf(List<PluginDescriptor> result, List<PluginDescriptor> sources, 579 PluginDescriptor plugin) 580 { 581 // load plugin descriptor informations if not yet done 582 plugin.loadDescriptor(); 583 584 for (PluginIdent ident : plugin.getRequired()) 585 { 586 // already in our dependences ? --> pass to the next one 587 if ((ident == null) || (PluginDescriptor.getPlugin(result, ident, true) != null)) 588 continue; 589 590 // find sources dependent plugin 591 final PluginDescriptor dep = PluginDescriptor.getPlugin(sources, ident, true); 592 593 // dependence found ? 594 if (dep != null) 595 { 596 // and add it to list 597 PluginDescriptor.addToList(result, dep); 598 // search its dependencies too 599 getLocalDependenciesOf(result, sources, dep); 600 } 601 } 602 } 603 604 /** 605 * Reorder the list so needed dependencies comes first in list 606 */ 607 public static List<PluginDescriptor> orderDependencies(List<PluginDescriptor> plugins) 608 { 609 final List<PluginDescriptor> sources = new ArrayList<PluginDescriptor>(plugins); 610 final List<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 611 612 while (sources.size() > 0) 613 { 614 final List<PluginDescriptor> deps = new ArrayList<PluginDescriptor>(); 615 616 getLocalDependenciesOf(result, sources, sources.get(0)); 617 618 // add last to first dep 619 for (int i = deps.size() - 1; i >= 0; i--) 620 PluginDescriptor.addToList(result, deps.get(i)); 621 622 // then add plugin 623 PluginDescriptor.addToList(result, sources.get(0)); 624 625 // remove tested plugin and its dependencies from source 626 sources.removeAll(result); 627 } 628 629 return result; 630 } 631 632 /** 633 * Resolve dependencies for specified plugin 634 * 635 * @param taskFrame 636 */ 637 public static boolean getDependencies(PluginDescriptor plugin, List<PluginDescriptor> pluginsToInstall, 638 CancelableProgressFrame taskFrame, boolean showError) 639 { 640 // load plugin descriptor informations if not yet done 641 plugin.loadDescriptor(); 642 643 // check dependencies 644 for (PluginIdent ident : plugin.getRequired()) 645 { 646 if ((taskFrame != null) && taskFrame.isCancelRequested()) 647 return false; 648 649 // should not happen but... 650 if (ident == null) 651 continue; 652 653 // already in our dependencies ? --> pass to the next one 654 if (PluginDescriptor.getPlugin(pluginsToInstall, ident, true) != null) 655 continue; 656 657 final String className = ident.getClassName(); 658 659 // get local & online plugin 660 final PluginDescriptor localPlugin = PluginLoader.getPlugin(className); 661 final PluginDescriptor onlinePlugin = PluginRepositoryLoader.getPlugin(className); 662 663 // plugin not yet installed or outdated ? 664 if ((localPlugin == null) || ident.getVersion().isGreater(localPlugin.getVersion())) 665 { 666 // online plugin not found ? 667 if (onlinePlugin == null) 668 { 669 // error 670 if (showError) 671 { 672 System.err.println("Can't resolve dependencies for plugin '" + plugin.getName() + "' :"); 673 674 if (localPlugin == null) 675 System.err.println("Plugin class '" + ident.getClassName() + " not found !"); 676 else 677 { 678 System.err.println(localPlugin.getName() + " " + localPlugin.getVersion() + " installed"); 679 System.err.println("but version " + ident.getVersion() + " or greater needed."); 680 } 681 } 682 683 return false; 684 } 685 // online plugin version incorrect 686 else if (ident.getVersion().isGreater(onlinePlugin.getVersion())) 687 { 688 // error 689 if (showError) 690 { 691 System.err.println("Can't resolve dependencies for plugin '" + plugin.getName() + "' :"); 692 System.err.println( 693 onlinePlugin.getName() + " " + onlinePlugin.getVersion() + " found in repository"); 694 System.err.println("but version " + ident.getVersion() + " or greater needed."); 695 } 696 697 return false; 698 } 699 700 // add to the install list 701 PluginDescriptor.addToList(pluginsToInstall, onlinePlugin); 702 // and check dependencies for this plugin 703 if (!getDependencies(onlinePlugin, pluginsToInstall, taskFrame, showError)) 704 return false; 705 } 706 else 707 { 708 // just check if we have update for dependency 709 if ((onlinePlugin != null) && (localPlugin.getVersion().isLower(onlinePlugin.getVersion()))) 710 { 711 // as web site doesn't handle version dependency, we force the update 712 713 // add to the install list 714 PluginDescriptor.addToList(pluginsToInstall, onlinePlugin); 715 // and check dependencies for this plugin 716 if (!getDependencies(onlinePlugin, pluginsToInstall, taskFrame, showError)) 717 return false; 718 } 719 } 720 } 721 722 return true; 723 } 724 725 private void installInternal() 726 { 727 DownloadFrame taskFrame = null; 728 729 try 730 { 731 final List<PluginInstallInfo> infos; 732 boolean showProgress; 733 734 synchronized (installFIFO) 735 { 736 infos = new ArrayList<PluginInstaller.PluginInstallInfo>(installFIFO); 737 738 showProgress = false; 739 for (int i = infos.size() - 1; i >= 0; i--) 740 { 741 final PluginInstallInfo info = infos.get(i); 742 743 PluginDescriptor.addToList(installingPlugins, info.plugin); 744 showProgress |= info.showProgress; 745 } 746 747 installFIFO.clear(); 748 } 749 750 if (showProgress && !Icy.getMainInterface().isHeadLess()) 751 { 752 taskFrame = new DownloadFrame(); 753 taskFrame.setMessage("Initializing..."); 754 } 755 756 List<PluginDescriptor> dependencies = new ArrayList<PluginDescriptor>(); 757 final Set<PluginDescriptor> pluginsOk = new HashSet<PluginDescriptor>(); 758 final Set<PluginDescriptor> pluginsNOk = new HashSet<PluginDescriptor>(); 759 final Set<PluginDescriptor> pluginsNewJava = new HashSet<PluginDescriptor>(); 760 761 // get dependencies 762 for (int i = installingPlugins.size() - 1; i >= 0; i--) 763 { 764 final PluginDescriptor plugin = installingPlugins.get(i); 765 final String plugDesc = plugin.getName() + " " + plugin.getVersion(); 766 767 if (taskFrame != null) 768 { 769 // cancel requested ? 770 if (taskFrame.isCancelRequested()) 771 return; 772 773 taskFrame.setMessage("Checking dependencies for '" + plugDesc + "' ..."); 774 } 775 776 // check dependencies 777 if (!getDependencies(plugin, dependencies, taskFrame, true)) 778 { 779 // can't resolve dependencies for this plugin 780 pluginsNOk.add(plugin); 781 installingPlugins.remove(i); 782 } 783 } 784 785 // nothing to install 786 if (installingPlugins.isEmpty()) 787 return; 788 789 // order dependencies 790 dependencies = orderDependencies(dependencies); 791 // add dependencies at the beginning of the installing list 792 for (PluginDescriptor plugin : dependencies) 793 PluginDescriptor.addToList(installingPlugins, plugin, 0); 794 795 String error = ""; 796 797 // clear backup folder 798 FileUtil.delete(Updater.BACKUP_DIRECTORY, true); 799 800 // now we can proceed the installation itself 801 for (PluginDescriptor plugin : installingPlugins) 802 { 803 for (PluginIdent ident : plugin.getRequired()) 804 { 805 // one of the dependencies was not correctly installed ? 806 if (PluginDescriptor.existInList(pluginsNOk, ident)) 807 { 808 // we can't install the plugin, continue with the next one 809 pluginsNOk.add(plugin); 810 continue; 811 } 812 } 813 814 final String plugDesc = plugin.getName() + " " + plugin.getVersion(); 815 816 if (taskFrame != null) 817 { 818 // cancel requested ? --> interrupt installation 819 if (taskFrame.isCancelRequested()) 820 break; 821 822 taskFrame.setMessage("Installing " + plugDesc + "..."); 823 } 824 825 try 826 { 827 // backup plugin 828 error = backup(plugin); 829 830 // backup ok --> install plugin 831 if (StringUtil.isEmpty(error)) 832 { 833 error = downloadAndSavePlugin(plugin, taskFrame); 834 835 // an error occurred ? --> restore 836 if (!StringUtil.isEmpty(error)) 837 Updater.restore(); 838 } 839 } 840 finally 841 { 842 // delete backup 843 FileUtil.delete(Updater.BACKUP_DIRECTORY, true); 844 } 845 846 if (StringUtil.isEmpty(error)) 847 pluginsOk.add(plugin); 848 else 849 { 850 pluginsNOk.add(plugin); 851 // print error 852 System.err.println(error); 853 } 854 } 855 856 // verify installed plugins 857 if (taskFrame != null) 858 taskFrame.setMessage("Verifying plugins..."); 859 860 // reload plugin list 861 PluginLoader.reload(); 862 863 for (PluginDescriptor plugin : pluginsOk) 864 { 865 error = PluginLoader.verifyPlugin(plugin); 866 867 // send report when we have verification error 868 if (!StringUtil.isEmpty(error)) 869 { 870 final String mess = "Fatal error while loading '" + plugin.getClassName() + "' class from " 871 + plugin.getJarFilename() + " :\n" + error; 872 873 // new java version required ? 874 if (error.contains(PluginLoader.NEWER_JAVA_REQUIRED)) 875 { 876 // print error in console 877 System.err.println(mess); 878 // add to list 879 pluginsNewJava.add(plugin); 880 } 881 else 882 { 883 // report error to developer 884 IcyExceptionHandler.report(plugin, "An error occured while installing the plugin :\n" + error); 885 // print error 886 System.err.println(mess); 887 // add to list 888 pluginsNOk.add(plugin); 889 } 890 } 891 } 892 893 // remove all plugins which failed or require new version of Java from OK list 894 pluginsOk.removeAll(pluginsNOk); 895 pluginsOk.removeAll(pluginsNewJava); 896 897 if (!pluginsNOk.isEmpty()) 898 { 899 System.err.println(); 900 System.err.println("Installation of the following plugin(s) failed:"); 901 for (PluginDescriptor plugin : pluginsNOk) 902 { 903 System.err.println(plugin.getName() + " " + plugin.getVersion()); 904 // notify about installation fails 905 fireInstalledEvent(plugin, false); 906 } 907 System.err.println(); 908 } 909 910 if (!pluginsOk.isEmpty()) 911 { 912 System.out.println(); 913 System.out.println("The following plugin(s) has been correctly installed:"); 914 for (PluginDescriptor plugin : pluginsOk) 915 { 916 System.out.println(plugin.getName() + " " + plugin.getVersion()); 917 // notify about installation successes 918 fireInstalledEvent(plugin, true); 919 } 920 System.out.println(); 921 } 922 923 if (!pluginsNewJava.isEmpty()) 924 { 925 System.err.println(); 926 System.err.println("The following plugin(s) require a newer version of java:"); 927 for (PluginDescriptor plugin : pluginsNewJava) 928 { 929 System.err.println(plugin.getName() + " " + plugin.getVersion()); 930 // notify about installation even fails 931 fireInstalledEvent(plugin, false); 932 } 933 System.err.println(); 934 } 935 936 if (showProgress && !Icy.getMainInterface().isHeadLess()) 937 { 938 // no plugin success ? 939 if (pluginsOk.isEmpty()) 940 new FailedAnnounceFrame("Plugin(s) installation failed !", 10); 941 // no plugin fail ? 942 else if (pluginsNOk.isEmpty() && pluginsNewJava.isEmpty()) 943 new SuccessfullAnnounceFrame("Plugin(s) installation was successful !", 10); 944 else if (!pluginsNOk.isEmpty()) 945 new FailedAnnounceFrame( 946 "Some plugin(s) installation failed (looks at the output console for detail) !", 10); 947 948 // notify about new java version required 949 for (PluginDescriptor plugin : pluginsNewJava) 950 new AnnounceFrame("Plugin '" + plugin.getName() + " requires a new version of Java !", 10); 951 } 952 } 953 finally 954 955 { 956 // installation end 957 installingPlugins.clear(); 958 if (taskFrame != null) 959 taskFrame.close(); 960 } 961 } 962 963 private void desinstallInternal() 964 { 965 CancelableProgressFrame taskFrame = null; 966 967 try 968 { 969 final List<PluginInstallInfo> infos; 970 boolean showProgress; 971 972 synchronized (removeFIFO) 973 { 974 infos = new ArrayList<PluginInstaller.PluginInstallInfo>(removeFIFO); 975 976 // determine if we should display the progress bar 977 showProgress = false; 978 for (int i = infos.size() - 1; i >= 0; i--) 979 { 980 final PluginInstallInfo info = infos.get(i); 981 982 desinstallingPlugin.add(info.plugin); 983 showProgress |= info.showProgress; 984 } 985 986 removeFIFO.clear(); 987 } 988 989 if (showProgress && !Icy.getMainInterface().isHeadLess()) 990 taskFrame = new CancelableProgressFrame("Initializing..."); 991 992 // now we can proceed remove 993 for (PluginDescriptor plugin : desinstallingPlugin) 994 { 995 final String plugDesc = plugin.getName() + " " + plugin.getVersion(); 996 final boolean result; 997 998 if (taskFrame != null) 999 { 1000 // cancel requested ? 1001 if (taskFrame.isCancelRequested()) 1002 return; 1003 1004 taskFrame.setMessage("Removing plugin '" + plugDesc + "'..."); 1005 } 1006 1007 result = deletePlugin(plugin); 1008 1009 // notify plugin deletion 1010 fireRemovedEvent(plugin, result); 1011 1012 if (showProgress && !Icy.getMainInterface().isHeadLess()) 1013 { 1014 if (!result) 1015 new FailedAnnounceFrame("Plugin '" + plugDesc + "' delete operation failed !"); 1016 } 1017 1018 if (result) 1019 System.out.println("Plugin '" + plugDesc + "' correctly removed."); 1020 else 1021 System.err.println("Plugin '" + plugDesc + "' delete operation failed !"); 1022 } 1023 } 1024 finally 1025 { 1026 if (taskFrame != null) 1027 taskFrame.close(); 1028 // removing end 1029 desinstallingPlugin.clear(); 1030 } 1031 1032 // reload plugin list 1033 PluginLoader.reload(); 1034 } 1035 1036 /** 1037 * Add a listener 1038 * 1039 * @param listener 1040 */ 1041 public static void addListener(PluginInstallerListener listener) 1042 { 1043 synchronized (instance.listeners) 1044 { 1045 instance.listeners.add(PluginInstallerListener.class, listener); 1046 } 1047 } 1048 1049 /** 1050 * Remove a listener 1051 * 1052 * @param listener 1053 */ 1054 public static void removeListener(PluginInstallerListener listener) 1055 { 1056 synchronized (instance.listeners) 1057 { 1058 instance.listeners.remove(PluginInstallerListener.class, listener); 1059 } 1060 } 1061 1062 /** 1063 * fire plugin installed event 1064 */ 1065 private void fireInstalledEvent(PluginDescriptor plugin, boolean success) 1066 { 1067 synchronized (listeners) 1068 { 1069 for (PluginInstallerListener listener : listeners.getListeners(PluginInstallerListener.class)) 1070 listener.pluginInstalled(plugin, success); 1071 } 1072 } 1073 1074 /** 1075 * fire plugin removed event 1076 */ 1077 private void fireRemovedEvent(PluginDescriptor plugin, boolean success) 1078 { 1079 synchronized (listeners) 1080 { 1081 for (PluginInstallerListener listener : listeners.getListeners(PluginInstallerListener.class)) 1082 listener.pluginRemoved(plugin, success); 1083 } 1084 } 1085 1086}