001 /* 002 Copyright (C) 2003 Adam Olsen 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU General Public License as published by 006 the Free Software Foundation; either version 1, or (at your option) 007 any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU General Public License for more details. 013 014 You should have received a copy of the GNU General Public License 015 along with this program; if not, write to the Free Software 016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 017 */ 018 019 package com.valhalla.pluginmanager; 020 021 import javax.swing.*; 022 import javax.swing.event.*; 023 import java.awt.*; 024 import java.awt.event.*; 025 import java.io.*; 026 import java.net.*; 027 import java.util.*; 028 import com.valhalla.gui.*; 029 030 /** 031 * Allows the user to download, install, and upgrade plugins all from 032 * within the application that the plugins are used in 033 * 034 * @author Adam Olsen 035 * @version 1.0 036 */ 037 public class PluginManager extends JDialog 038 { 039 private ResourceBundle resources = 040 ResourceBundle.getBundle( "PluginManager", Locale.getDefault() ); 041 private String mirror; 042 private String script; 043 private String installDir; 044 045 private ArrayList pluginList = null; 046 private ArrayList installedPlugins = null; 047 private ArrayList upgradeList = null; 048 049 private JLabel statusLabel = new JLabel( "<html><b>" + resources.getString( "status" ) + "</b>: " ); 050 private JTabbedPane pane = new JTabbedPane(); 051 private JButton closeButton = new JButton( resources.getString( "closeButton" ) ); 052 private JPanel mainPanel; 053 private JPanel bottomPanel = new JPanel( new BorderLayout( 5, 5 ) ); 054 private PluginManagerPanel listPanel = new PluginManagerPanel( this, false ); 055 private PluginManagerPanel managePanel = new PluginManagerPanel( this, true ); 056 private PluginManagerPanel upgradePanel = new PluginManagerPanel( this, false ); 057 private JButton unloadButton = new JButton( resources.getString( "unload" ) ); 058 private JButton loadButton = new JButton( resources.getString( "load" ) ); 059 060 private PluginManager thisPointer = this; 061 062 /** 063 * Constructs the Plugin Manager 064 * @param mirror the mirror to download the plugins from 065 * @param script the script to get after connecting 066 * @param installDir the location to install the plugins to 067 */ 068 public PluginManager( String mirror, String script, String installDir ) 069 { 070 setTitle( resources.getString( "pluginManager" ) ); 071 this.mirror = mirror; 072 this.script = script; 073 this.installDir = installDir; 074 075 initComponents(); 076 DialogTracker.addDialog( this, false, true ); 077 downloadPluginList(); 078 } 079 080 /** 081 * @return the download mirror 082 */ 083 String getMirror() { return mirror; } 084 085 /** 086 * @return the download script 087 */ 088 String getScript() { return script; } 089 090 /** 091 * @return the install dir 092 */ 093 String getInstallDir() { return installDir; } 094 095 /** 096 * Sets up UI components 097 */ 098 private void initComponents() 099 { 100 closeButton.addActionListener( new ActionListener() 101 { 102 public void actionPerformed( ActionEvent e ) { DialogTracker.removeDialog( thisPointer ); } 103 } ); 104 105 mainPanel = (JPanel)getContentPane(); 106 mainPanel.setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) ); 107 mainPanel.setLayout( new BorderLayout( 5, 5 ) ); 108 loadButton.setEnabled( false ); 109 unloadButton.setEnabled( false ); 110 111 ListActionListener listener = new ListActionListener(); 112 113 managePanel.getButton().addActionListener( listener ); 114 managePanel.getTable().getSelectionModel().addListSelectionListener( new HighlightListener() ); 115 unloadButton.addActionListener( listener ); 116 loadButton.addActionListener( listener ); 117 118 listPanel.getButton().addActionListener( listener ); 119 upgradePanel.getButton().addActionListener( listener ); 120 121 // set up the manage panel 122 managePanel.setButtonText( resources.getString( "removePlugins" ) ); 123 JPanel bPanel = managePanel.getButtonPanel(); 124 bPanel.add( Box.createRigidArea( new Dimension( 5, 0 ) ) ); 125 bPanel.add( loadButton ); 126 bPanel.add( unloadButton ); 127 bPanel.validate(); 128 129 upgradePanel.setButtonText( resources.getString( "upgradePlugins" ) ); 130 131 pane.add( managePanel, resources.getString( "managePlugins" ) ); 132 pane.add( listPanel, resources.getString( "installPlugins" ) ); 133 pane.add( upgradePanel, resources.getString( "upgradePlugins" ) ); 134 135 mainPanel.add( pane, BorderLayout.CENTER ); 136 mainPanel.add( bottomPanel, BorderLayout.SOUTH ); 137 138 bottomPanel.add( statusLabel, BorderLayout.CENTER ); 139 bottomPanel.add( closeButton, BorderLayout.EAST ); 140 141 pack(); 142 setSize( new Dimension( 520, 470 ) ); 143 setLocationRelativeTo( null ); 144 } 145 146 class HighlightListener implements ListSelectionListener 147 { 148 public void valueChanged( ListSelectionEvent e ) 149 { 150 int rows[] = managePanel.getTable().getSelectedRows(); 151 152 boolean selected = ( rows.length > 0 ); 153 managePanel.getButton().setEnabled( selected ); 154 loadButton.setEnabled( selected ); 155 unloadButton.setEnabled( selected ); 156 } 157 } 158 159 /** 160 * Listens for a button to be clicked in one of the list panels 161 */ 162 class ListActionListener implements ActionListener 163 { 164 public void actionPerformed( ActionEvent e ) 165 { 166 if( e.getSource() == managePanel.getButton() ) removeHandler(); 167 else if( e.getSource() == listPanel.getButton() ) installHandler( pluginList ); 168 else if( e.getSource() == upgradePanel.getButton() ) installHandler( upgradeList ); 169 else if( e.getSource() == unloadButton ) 170 { 171 unloadPluginHandler( installedPlugins, true, managePanel ); 172 managePanel.setPlugins( installedPlugins ); 173 } 174 else if( e.getSource() == loadButton ) 175 { 176 loadPluginHandler( installedPlugins, managePanel ); 177 managePanel.setPlugins( installedPlugins ); 178 } 179 } 180 } 181 182 private boolean checkSelectedRow( PluginManagerPanel panel, int current ) 183 { 184 if( panel == null ) return false; 185 JTable table = panel.getTable(); 186 187 int rows[] = table.getSelectedRows(); 188 for( int i = 0; i < rows.length; i++ ) 189 { 190 if( current == rows[i] ) return true; 191 } 192 193 return false; 194 } 195 196 /** 197 * loads the checked plugins 198 */ 199 private void loadPluginHandler( ArrayList list, PluginManagerPanel panel ) 200 { 201 for( int i = 0; i < list.size(); i++ ) 202 { 203 Properties props = (Properties)list.get( i ); 204 if( props.getProperty( "selected" ) != null || checkSelectedRow( panel, i ) ) 205 { 206 Hashtable loadedPlugins = PluginLoader.getInstance().getLoadedPlugins(); 207 PluginJAR jar = PluginLoader.getInstance().getPlugin( props.getProperty( "name" ) ); 208 209 if( jar != null && !jar.getLoaded() ) 210 { 211 jar.loadPlugin(); 212 loadedPlugins.put( props.getProperty( "name" ), jar ); 213 props.remove( "selected" ); 214 } 215 } 216 } 217 } 218 219 /** 220 * Unloads the checked plugins 221 */ 222 private void unloadPluginHandler( ArrayList list, boolean mark, PluginManagerPanel panel ) 223 { 224 for( int i = 0; i < list.size(); i++ ) 225 { 226 Properties props = (Properties)list.get( i ); 227 if( props.getProperty( "selected" ) != null || checkSelectedRow( panel, i ) ) 228 { 229 Hashtable loadedPlugins = PluginLoader.getInstance().getLoadedPlugins(); 230 231 PluginJAR jar = PluginLoader.getInstance().getPlugin( props.getProperty( "name" ) ); 232 233 if( jar != null ) 234 { 235 if( jar.getLoaded() ) jar.unloadPlugin(); 236 loadedPlugins.remove( props.getProperty( "name" ) ); 237 238 jar.close(); 239 240 jar = null; 241 System.gc(); 242 } 243 244 if( mark ) props.remove( "selected" ); 245 } 246 } 247 } 248 249 /** 250 * Installs the plugins selected in the install panel 251 * @param list the list to download 252 */ 253 private void installHandler( ArrayList list ) 254 { 255 setStatusText( resources.getString( "downloading" ) ); 256 Thread thread = new Thread( new PluginDownloaderThread( this, list ) ); 257 thread.start(); 258 } 259 260 /** 261 * Removes the plugins that are selected in the manage panel 262 */ 263 private void removeHandler() 264 { 265 int result = JOptionPane.showConfirmDialog( null, 266 resources.getString( "pluginRemoveConfirmation" ), 267 resources.getString( "pluginManager" ), 268 JOptionPane.YES_NO_OPTION ); 269 270 if( result != 0 ) return; 271 272 int rows[] = managePanel.getTable().getSelectedRows(); 273 ArrayList remove = new ArrayList(); 274 for( int i = 0; i < rows.length; i++ ) 275 { 276 Properties props = (Properties)installedPlugins.get( rows[i] ); 277 props.setProperty( "selected", "true" ); 278 279 remove.add( props ); 280 } 281 282 unloadPluginHandler( remove, false, null ); 283 System.gc(); 284 remove = new ArrayList(); 285 286 for( int i = 0; i < rows.length; i++ ) 287 { 288 Properties props = (Properties)installedPlugins.get( rows[i] ); 289 Hashtable loadedPlugins = PluginLoader.getInstance().getLoadedPlugins(); 290 291 String name = props.getProperty( "fileName" ); 292 File file = new File( name ); 293 294 if( file.delete() ) 295 { 296 remove.add( props ); 297 } 298 else { 299 throwError( "Could not unload plugin!", false ); 300 return; 301 } 302 } 303 304 for( int i = 0; i < remove.size(); i++ ) 305 { 306 installedPlugins.remove( remove.get( i ) ); 307 } 308 309 managePanel.setPlugins( installedPlugins ); 310 downloadPluginList(); 311 } 312 313 /** 314 * Downloads a list of plugins 315 */ 316 private void downloadPluginList() 317 { 318 PluginLoader loader = PluginLoader.getInstance(); 319 loader.findPlugins( installDir + File.separatorChar + "plugins" ); 320 321 installedPlugins = loader.getInstalledPlugins(); 322 managePanel.setPlugins( installedPlugins ); 323 324 setStatusText( resources.getString( "gettingPluginList" ) ); 325 Thread thread = new Thread( new DownloadListThread() ); 326 thread.start(); 327 } 328 329 /** 330 * Called when the download thread is done downloading selected plugins 331 */ 332 protected void doneDownloadingPlugins( ArrayList list ) 333 { 334 boolean upgrading = false; 335 if( list == upgradeList ) upgrading = true; 336 337 File cacheDir = new File( installDir, "downloadcache" ); 338 File pluginDir = new File( installDir, "plugins" ); 339 if( !pluginDir.isDirectory() && !pluginDir.mkdirs() ) 340 { 341 throwError( "pluginDirectoryCreationError", true ); 342 return; 343 } 344 345 unloadPluginHandler( list, false, null ); 346 System.gc(); 347 348 for( int i = 0; i < list.size(); i++ ) 349 { 350 Properties props = (Properties)list.get( i ); 351 if( props.getProperty( "selected" ) != null ) 352 { 353 File file = new File( cacheDir, props.getProperty( "fileName" ) ); 354 File newFile = new File( pluginDir, props.getProperty( "fileName" ) ); 355 if( newFile.exists() && !newFile.delete() ) 356 { 357 com.valhalla.Logger.debug( "Could not delete old plugin!" ); 358 } 359 360 if( !file.renameTo( newFile ) ) 361 { 362 com.valhalla.Logger.debug( "Could not rename to new plugin" ); 363 } 364 } 365 } 366 367 PluginLoader.getNewInstance(); 368 System.gc(); 369 370 PluginLoader.getInstance().findPlugins( installDir + File.separatorChar + "plugins" ); 371 loadPluginHandler( list, null ); 372 373 downloadPluginList(); 374 } 375 376 /** 377 * Called when the downloader thread is done downloading the list of available plugins 378 */ 379 protected void doneDownloadingList() 380 { 381 listPanel.setPlugins( pluginList ); 382 upgradePanel.setPlugins( upgradeList ); 383 384 setStatusText( resources.getString( "doneDownloading" ) ); 385 } 386 387 /** 388 * Sets the text in the status label 389 * @param text the text to set 390 */ 391 private void setStatusText( String text ) 392 { 393 statusLabel.setText( "<html><b>" + resources.getString( "status" ) + "</b>: " + text ); 394 } 395 396 /** 397 * Returns true if a plugin is already installed 398 * @return <tt>true</tt> if a plugin is already installed 399 */ 400 private boolean pluginInstalled( Properties props ) 401 { 402 boolean check = false; 403 if( props.getProperty( "name" ) == null ) return check; 404 for( int i = 0; i < installedPlugins.size(); i++ ) 405 { 406 Properties p = (Properties)installedPlugins.get( i ); 407 408 if( p.getProperty( "name" ) != null && 409 p.getProperty( "name" ).equals( props.getProperty( "name" ) ) ) 410 check = true; 411 } 412 413 return check; 414 } 415 416 /** 417 * Returns true if a plugin is upgradable 418 * @return <tt>true</tt> if a plugin is already upgradable 419 */ 420 private boolean pluginUpgradable( Properties props ) 421 { 422 boolean check = false; 423 if( props.getProperty( "name" ) == null ) return check; 424 for( int i = 0; i < installedPlugins.size(); i++ ) 425 { 426 Properties p = (Properties)installedPlugins.get( i ); 427 428 if( p.getProperty( "name" ) != null && 429 p.getProperty( "name" ).equals( props.getProperty( "name" ) ) ) 430 { 431 try { 432 String installedVersion = p.getProperty( "version" ); 433 String remoteVersion = props.getProperty( "version" ); 434 435 int comp = remoteVersion.compareTo( installedVersion ); 436 437 if( comp > 0 ) check = true; 438 } 439 catch( Exception e ) { } 440 } 441 } 442 443 return check; 444 } 445 446 /** 447 * Collects the list of available plugins in their versions 448 */ 449 class DownloadListThread implements Runnable 450 { 451 public void run() 452 { 453 Socket socket = null; 454 BufferedReader in = null; 455 PrintWriter out = null; 456 pluginList = new ArrayList(); 457 upgradeList = new ArrayList(); 458 459 try { 460 socket = new Socket( mirror, 80 ); 461 out = new PrintWriter( socket.getOutputStream(), true ); 462 in = new BufferedReader( new InputStreamReader( socket.getInputStream() ) ); 463 464 out.println( "GET " + script + "?command=pluginList&apiVersion=" + PluginLoader.getAPIVersion() + " HTTP/1.0" ); 465 out.println( "Host: " + mirror + "\n" ); 466 467 String line = null; 468 String names[] = null; 469 int lineCount = 0; 470 471 while( ( line = in.readLine() ) != null ) 472 { 473 if( line.equals( "" ) ) break; 474 } 475 476 while( ( line = in.readLine() ) != null ) 477 { 478 // the first line will indicate if we've connected successfully 479 if( lineCount == 0 ) 480 { 481 if( !line.equals( "Plugin list will follow" ) ) 482 { 483 throwError( "invalidResponse", true ); 484 com.valhalla.Logger.debug( line ); 485 break; 486 } 487 } 488 489 // second line gets a list of available arguments for each plugin 490 else if( lineCount == 1 ) names = line.split( "\t" ); 491 492 // all lines after are plugin information lines 493 else { 494 String de[] = line.split( "\t" ); 495 //com.valhalla.Logger.debug( line ); 496 if( de.length == names.length ) 497 { 498 Properties props = new Properties(); 499 for( int i = 0; i < names.length; i++ ) 500 { 501 props.setProperty( names[i], de[i] ); 502 } 503 504 boolean ok = true; 505 String os = props.getProperty( "os" ); 506 String arch = props.getProperty( "arch" ); 507 508 if( os != null && !os.equals( "all" ) ) 509 { 510 if( !System.getProperty( "os.name" ).startsWith( os ) ) ok = false; 511 } 512 513 if( arch != null && !arch.equals( "all" ) ) 514 { 515 if( !System.getProperty( "os.arch" ).equals( arch ) ) ok = false; 516 } 517 518 // make sure the plugin api version is the same, otherwise 519 // ignore this plugin 520 if( ok && props.getProperty( "APIVersion" ).equals( PluginLoader.getAPIVersion() + "" ) ) 521 { 522 if( !pluginInstalled( props ) ) pluginList.add( props ); 523 else if( pluginUpgradable( props ) ) upgradeList.add( props ); 524 } 525 } 526 else { 527 com.valhalla.Logger.debug( "Invalid plugin found." ); 528 } 529 } 530 531 lineCount++; 532 } 533 534 socket.close(); 535 } 536 catch( Exception e ) 537 { 538 throwError( e.getMessage(), false ); 539 e.printStackTrace(); 540 } 541 542 doneDownloadingList(); 543 } 544 } 545 546 /** 547 * Displays an error dialog 548 * @param message the message to display 549 */ 550 void throwError( String message, boolean useResources ) 551 { 552 if( useResources ) message = resources.getString( message ); 553 final String tempMessage = message; 554 555 SwingUtilities.invokeLater( new Runnable() 556 { 557 public void run() 558 { 559 Standard.warningMessage( thisPointer, resources.getString( "pluginManager" ), tempMessage ); 560 setStatusText( tempMessage ); 561 } 562 } ); 563 } 564 }