source: release-kits/shared/core/uninstaller/Uninstaller.java@ 20648

Last change on this file since 20648 was 20648, checked in by oranfry, 15 years ago

don't check for existence of files before deleting in uninstaller, as symlinks show up as non-existent if their targets are non-existent (which is likely if you're halfway through an uninstall)

File size: 16.6 KB
Line 
1import java.util.ResourceBundle;
2import java.awt.*;
3import java.awt.event.*;
4import javax.swing.JFrame;
5import javax.swing.JPanel;
6import javax.swing.JCheckBox;
7import javax.swing.JLabel;
8import javax.swing.JButton;
9import javax.swing.BoxLayout;
10import javax.swing.JTextArea;
11import javax.swing.JScrollPane;
12import javax.swing.border.EmptyBorder;
13import javax.swing.JOptionPane;
14import javax.swing.Box;
15import javax.swing.JDialog;
16import javax.swing.JMenuItem;
17import javax.swing.JPopupMenu;
18
19import java.io.File;
20import java.io.BufferedReader;
21import java.io.FileReader;
22import java.io.FileNotFoundException;
23import java.io.FileWriter;
24import java.io.IOException;
25
26import java.util.regex.Pattern;
27import java.util.regex.Matcher;
28import java.util.ArrayList;
29
30import javax.swing.SwingUtilities;
31
32import java.util.Enumeration;
33import java.awt.Font;
34import javax.swing.plaf.FontUIResource;
35import javax.swing.UIManager;
36
37
38
39public class Uninstaller {
40
41 public static int SCREEN_WIDTH = 600;
42 public static int SCREEN_HEIGHT = 450;
43
44 public static final ResourceBundle bundle = ResourceBundle.getBundle("resources.LanguagePack");
45
46 public static final File gs2InstallProps = new File("etc/installation.properties");
47 public static final File gs3InstallProps = new File("installation.properties");
48
49 boolean keepCollections = true;
50 //boolean keepModifiedFiles = false;
51 boolean ignoreReadOnlys = false;
52
53 JFrame frame;
54 JCheckBox keepCollectionsCheckbox;
55 //JCheckBox keepModifiedFilesCheckbox;
56
57 //panels
58 JPanel progressPanel;
59 JPanel introPanel;
60
61 //toolbars
62 JPanel initialToolbar;
63 JPanel finishToolbar;
64
65
66 JScrollPane logPane;
67 FollowingJTextArea log;
68 JButton uninstallButton;
69 JButton finishButton;
70
71 boolean confirmationGiven = false;
72 Thread mainThread = null;
73
74 public static void main( String[] args ) {
75 (new Uninstaller()).go();
76 }
77
78 public void go() {
79
80 //set font
81 String new_default_font_str = null;
82 if ( System.getProperty("os.name").equals("Linux") ) {
83 new_default_font_str = "Bitstream Cyberbit";
84 } else if ( System.getProperty("os.name").startsWith("Windows") ) {
85 new_default_font_str = "Arial Unicode MS";
86 }
87
88 if ( new_default_font_str != null ) {
89
90 FontUIResource default_font = new FontUIResource(new_default_font_str, Font.PLAIN, 12);
91 Enumeration keys = UIManager.getDefaults().keys();
92 while (keys.hasMoreElements()) {
93 Object key = keys.nextElement();
94 Object value = UIManager.get(key);
95 if (value instanceof FontUIResource) {
96 UIManager.put(key, default_font);
97 }
98 }
99
100 }
101
102
103 mainThread = Thread.currentThread();
104
105 frame = new JFrame();
106 frame.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
107 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
108 frame.setLocation(screenSize.width/2 - frame.getWidth()/2, screenSize.height/2 - frame.getHeight()/2);
109
110 //The panel to introduce and ask for options
111 introPanel = new JPanel(new BorderLayout());
112 JPanel innerPanel = new JPanel();
113 innerPanel.setLayout( new BoxLayout(innerPanel,BoxLayout.Y_AXIS) );
114 innerPanel.setBorder( new EmptyBorder(10, 10, 5, 10) );
115 introPanel.add( BorderLayout.WEST, innerPanel );
116
117 //The panel to be displayed while the uninstall is happening
118 progressPanel = new JPanel(new BorderLayout());
119 log = new FollowingJTextArea();
120 log.setEditable(false);
121 logPane = new JScrollPane(log);
122 logPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
123 logPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
124 progressPanel.add( BorderLayout.NORTH, new JLabel("Progress") );
125 progressPanel.add( BorderLayout.CENTER, logPane );
126
127 //initial toolbar
128 initialToolbar = new JPanel();
129 uninstallButton = new JButton(bundle.getString("uninstaller.uninstall"));
130 uninstallButton.addActionListener( new StartUninstallListener() );
131 JButton cancelButton = new JButton(bundle.getString("uninstaller.cancel"));
132 cancelButton.addActionListener( new CancelListener() );
133 initialToolbar.add(uninstallButton);
134 initialToolbar.add(cancelButton);
135
136 //finish toolbar
137 finishToolbar = new JPanel();
138 finishButton = new JButton(bundle.getString("uninstaller.finish"));
139 finishButton.addActionListener( new FinishListener() );
140 finishButton.setEnabled( false );
141 finishToolbar.add( finishButton );
142
143
144 String pwd = (new File(".")).getAbsolutePath();
145 if ( pwd.endsWith("/.") ) {
146 pwd = pwd.substring( 0, pwd.length()-2 );
147 }
148
149 JLabel l;
150
151 l = new JLabel(bundle.getString("uninstaller.will.uninstall.from"));
152 innerPanel.add( l );
153 innerPanel.add( Box.createRigidArea(new Dimension(5,5)) );
154
155 l = new JLabel(" " + pwd);
156 l.setFont( new Font( "Monospaced", Font.BOLD, 14 ) );
157 innerPanel.add( l );
158 innerPanel.add( Box.createRigidArea(new Dimension(5,20)) );
159
160 l = new JLabel(bundle.getString("uninstaller.uninstall.options"));
161 innerPanel.add( l );
162
163 keepCollectionsCheckbox = new JCheckBox(bundle.getString("uninstaller.keep.collections"));
164 keepCollectionsCheckbox.setSelected(true);
165 innerPanel.add( keepCollectionsCheckbox );
166
167 //keepModifiedFilesCheckbox = new JCheckBox("Keep Modified Files");
168 //innerPanel.add( keepModifiedFilesCheckbox );
169
170 frame.setTitle( bundle.getString("uninstaller.greenstone.uninstaller") );
171 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
172
173 //the initial screen
174 frame.getContentPane().add( BorderLayout.CENTER, introPanel );
175 frame.getContentPane().add( BorderLayout.SOUTH, initialToolbar );
176
177 frame.setVisible(true);
178
179 boolean ready = precheck();
180 if ( !ready ) {
181 System.exit(0);
182 }
183
184 //block and wait for signal to do the uninstall
185 do {
186 try {
187 Thread.sleep( Long.MAX_VALUE );
188 } catch ( InterruptedException ie ) {
189 }
190 } while ( !confirmationGiven );
191
192 doUninstall();
193 finishButton.setEnabled( true );
194
195 }
196
197 class CancelListener implements ActionListener {
198 public void actionPerformed( ActionEvent e ) {
199 System.exit(0);
200 }
201 }
202
203 class FinishListener implements ActionListener {
204 public void actionPerformed( ActionEvent e ) {
205 System.exit(0);
206 }
207 }
208
209 class StartUninstallListener implements ActionListener {
210 public void actionPerformed( ActionEvent e ) {
211
212 //The dialog to ask "are you sure"
213 Object[] options = { bundle.getString("uninstaller.uninstall"), bundle.getString("uninstaller.cancel") };
214 int n = JOptionPane.showOptionDialog(
215 frame,
216 bundle.getString("uninstaller.are.you.sure"),
217 bundle.getString("uninstaller.confirmation"),
218 JOptionPane.YES_NO_CANCEL_OPTION,
219 JOptionPane.QUESTION_MESSAGE,
220 null,
221 options,
222 options[0]
223 );
224 if ( n == 1 ) {
225 return;
226 }
227
228 keepCollections = keepCollectionsCheckbox.isSelected();
229 //keepModifiedFiles = keepModifiedFilesCheckbox.isSelected();
230
231 //confirm delete of collections
232 if ( !keepCollections ) {
233 options[0] = bundle.getString("uninstaller.continue");
234 options[1] = bundle.getString("uninstaller.cancel");
235 n = JOptionPane.showOptionDialog(
236 frame,
237 bundle.getString("uninstaller.are.you.sure.collections"),
238 bundle.getString("uninstaller.confirmation"),
239 JOptionPane.YES_NO_CANCEL_OPTION,
240 JOptionPane.WARNING_MESSAGE,
241 null,
242 options,
243 options[0]
244 );
245 if ( n == 1 ) {
246 return;
247 }
248 }
249
250
251 introPanel.setVisible( false );
252 frame.getContentPane().remove(introPanel);
253 frame.getContentPane().add( BorderLayout.CENTER, progressPanel );
254
255 initialToolbar.setVisible( false );
256 frame.getContentPane().remove( initialToolbar );
257 frame.getContentPane().add( BorderLayout.SOUTH, finishToolbar);
258
259 confirmationGiven = true;
260 mainThread.interrupt();
261 }
262 }
263
264 /**
265 * Do some checks
266 */
267 public boolean precheck() {
268
269 if ( !gs2InstallProps.exists() && !gs3InstallProps.exists() ) {
270 log( bundle.getString("uninstaller.error.couldnt.find.install.props") + "\n" );
271 JOptionPane.showMessageDialog(frame, bundle.getString("uninstaller.error.couldnt.find.install.props"), bundle.getString("uninstaller.error"), 0);
272 return false;
273 }
274 return true;
275
276 }
277
278 public void log( String s ) {
279 SwingUtilities.invokeLater( new LogAppender( s ) );
280 //log.append( s );
281
282 }
283
284 /**
285 * Does the uninstall
286 */
287 public void doUninstall() {
288
289 File startMenuPath = null;
290
291 //delete the start menu group if present
292
293 if ( gs2InstallProps.exists() ) {
294 String smp = null;
295 try {
296 smp = getPropertyValue( "installed.startmenu.path", gs2InstallProps );
297 } catch ( Exception e ) {
298 System.err.println( e.getMessage() );
299 }
300 if ( smp != null ) {
301 startMenuPath = new File( smp );
302 }
303 } else if ( gs3InstallProps.exists() ) {
304 String smp = null;
305 try {
306 smp = getPropertyValue( "installed.startmenu.path", gs3InstallProps );
307 } catch ( Exception e ) {
308 System.err.println( e.getMessage() );
309 }
310 if ( smp != null ) {
311 startMenuPath = new File( smp );
312 }
313 }
314
315 if ( startMenuPath == null ) {
316
317 log( bundle.getString("uninstaller.info.no.startmenu") + "\n" );
318
319 } else {
320 log( "StartMenu Path: " + startMenuPath.getAbsolutePath() + "\n" );
321
322 try {
323 recursiveDelete( startMenuPath, null );
324 } catch ( CancelledException ce ) {
325 log( bundle.getString("uninstaller.cancelled") + "\n" );
326 changeToFinishToolbar();
327 JOptionPane.showMessageDialog(frame, bundle.getString("uninstaller.cancelled"), bundle.getString("uninstaller.complete"), 1);
328 return;
329 }
330 }
331
332 //delete the files
333 try {
334 ArrayList exceptions = new ArrayList();
335
336 //never delete the things we are currently running
337 exceptions.add( new File("bin/search4j.exe") );
338 exceptions.add( new File("bin/search4j") );
339
340 exceptions.add( new File("bin/windows/search4j.exe") );
341 exceptions.add( new File("bin/linux/search4j") );
342 exceptions.add( new File("bin/darwin/search4j") );
343
344 exceptions.add( new File("packages/jre") );
345 exceptions.add( new File("uninst.jar") );
346 exceptions.add( new File("Uninstall.bat") );
347 exceptions.add( new File("Uninstall.sh") );
348
349 if ( keepCollections ) {
350 exceptions.add( new File("web/sites/localsite/collect") );
351 exceptions.add( new File("collect") );
352 }
353
354
355 File cd = null;
356 try {
357 cd = new File( new File(".").getCanonicalPath() );
358 } catch ( Exception e ) {
359 JOptionPane.showMessageDialog(frame, bundle.getString("uninstaller.failed.to.figure.cd"), bundle.getString("uninstaller.error") , 0);
360 System.exit(0);
361 }
362
363 File[] ex = new File[exceptions.size()];
364 for ( int i=0; i<exceptions.size(); i++ ) {
365 ex[i] = (File)exceptions.get(i);
366 }
367
368 recursiveDelete( cd , ex );
369 } catch ( CancelledException ce ) {
370 log( bundle.getString("uninstaller.cancelled") + "\n" );
371 JOptionPane.showMessageDialog(frame, bundle.getString("uninstaller.cancelled"), bundle.getString("uninstaller.complete"), 1);
372 changeToFinishToolbar();
373 return;
374 }
375
376 //create the flag file to indicate the uninstaller wants jre and uninst.jar to be deleted
377 try {
378 (new File("uninst.flag")).createNewFile();
379 } catch (Exception e) {
380 log( bundle.getString("uninstaller.couldnt-create-flagfile") + "\n" );
381 }
382
383 changeToFinishToolbar();
384 log( bundle.getString("uninstaller.finished") );
385 JOptionPane.showMessageDialog(frame, bundle.getString("uninstaller.finished"), bundle.getString("uninstaller.complete"), 1);
386 }
387
388 class LogAppender implements Runnable {
389 String s;
390
391 public LogAppender( String s ) {
392 this.s = s;
393 }
394
395 public void run() {
396 log.append( s );
397 }
398
399 }
400
401 public String getPropertyValue( String propertyName, File file ) throws Exception {
402
403 String regex = "^" + propertyName.replaceAll("\\.","\\\\.") + "[:=]\\s*(.*)$";
404 Pattern wantedLine = Pattern.compile( regex );
405 BufferedReader in = null;
406 try {
407 in = new BufferedReader(new FileReader( file ));
408 } catch ( Exception e ) {
409 throw new Exception( "Error - couldn't open the properties file " + file );
410 }
411
412 String line, value = null;
413
414 try {
415 boolean found = false;
416 while ( (line = in.readLine()) != null && !found ) {
417
418 Matcher matcher = wantedLine.matcher( line );
419 if ( matcher.matches() ) {
420 //found the property
421 //System.err.println( "found the property" );
422 value = matcher.group(1);
423 value = value.replaceAll( "#.*", "" ).trim();
424 return value;
425 }
426
427 }
428
429 //close the input stream
430 //System.err.println( "close the input stream" );
431 in.close();
432
433 } catch ( Exception e ) {
434 throw new Exception( "Error - couldn't read from the properties file" );
435 }
436 return null;
437
438 }
439
440 public void recursiveDelete( File f, File[] exceptions ) throws CancelledException {
441
442 //log.append( "Processing: " + f.getAbsolutePath() + "\n" );
443
444 // if this is an exception, return
445 if ( exceptions != null ) {
446 for ( int i=0; i<exceptions.length; i++ ) {
447 //System.out.println( "Comparing '" + f.getAbsolutePath() + "' with '" + exceptions[i].getAbsolutePath() + "'" );
448 try {
449 if ( f.equals( exceptions[i] ) || f.getCanonicalPath().equals(exceptions[i].getCanonicalPath()) ) {
450 log( Strings.replaceAll( bundle.getString("uninstaller.info.skipping"), "{file}", f.getAbsolutePath() ) + "\n" );
451 return;
452 }
453 } catch ( Exception e ) {
454 System.err.println("ERROR: Failed to resolve a path");
455 return;
456 }
457 }
458 }
459
460 //if it is a directory, recurse
461 if (f.isDirectory()) {
462 File[] files = f.listFiles();
463 if ( files != null && files.length > 0) {
464 for ( int i=0; i<files.length; i++ ) {
465 try {
466 recursiveDelete( files[i], exceptions );
467 } catch ( CancelledException ce ) {
468 throw new CancelledException();
469 }
470 }
471 }
472 }
473
474 //if this is now an empty directory, or a file, delete it
475 boolean doDelete = true;
476 if ( f.isDirectory() ) {
477 File[] files = f.listFiles();
478 doDelete = ( files == null || files.length == 0 );
479 }
480
481 if ( doDelete ) {
482
483 log( Strings.replaceAll( bundle.getString("uninstaller.deleting"), "{file}", f.getAbsolutePath() ) + "\n" );
484
485 if ( !f.delete() ) {
486 log( "*********\n" + Strings.replaceAll( bundle.getString("uninstaller.warning.couldnt.delete"), "{file}", f.getAbsolutePath() ) + "\n*********\n" );
487 return;
488 }
489 }
490
491 }
492
493 class CancelledException extends Exception {}
494
495 public void changeToFinishToolbar() {
496 initialToolbar.setVisible(false);
497 frame.getContentPane().remove(initialToolbar);
498 frame.getContentPane().add( BorderLayout.SOUTH, finishToolbar );
499 finishToolbar.setVisible(true);
500 }
501
502}
503
504class Strings {
505 static String replaceAll( String s, String nugget, String replacement ) {
506 StringBuffer string = new StringBuffer(s);
507 StringBuffer r = new StringBuffer();
508
509 int io = 0;
510 while ( (io=string.toString().indexOf(nugget)) != -1 ) {
511 r.append( string.toString().substring( 0, io ) );
512 r.append( replacement );
513 string.delete( 0, io + nugget.length() );
514 }
515 r.append( string.toString() );
516 return r.toString();
517 }
518}
519
520class FollowingJTextArea extends JTextArea {
521
522 private boolean follow = true;
523
524 public FollowingJTextArea() {
525 jInit();
526 }
527
528
529 private void jInit(){
530 final JPopupMenu popUp = getPopupMenu();
531 this.add(popUp);
532 this.addMouseListener(new MouseAdapter(){
533 public void mouseClicked(MouseEvent e) {
534 if (e.getButton() == e.BUTTON3) {
535 popUp.show(FollowingJTextArea.this,e.getX(),e.getY());
536 }
537 }
538 });
539 }
540
541 public boolean isFollow() {
542 return follow;
543 }
544 public void setFollow(boolean follow) {
545 this.follow = follow;
546 }
547
548 private void scrollToEnd(){
549 setCaretPosition(getDocument().getLength());
550 }
551 private void toggleFollow(){
552 setFollow(!isFollow());
553 }
554
555 /**
556 * Appends the given text to the end of the document.
557 *
558 * @param str the text to insert
559 * @todo Implement this javax.swing.JTextArea method
560 */
561 public void append(String str) {
562 super.append(str);
563 if(follow)scrollToEnd();
564 }
565 private JPopupMenu getPopupMenu() {
566 JPopupMenu contextMenu = new JPopupMenu("Options");
567
568 JMenuItem toggleFollowMenu = new JMenuItem("Toggle Follow");
569 toggleFollowMenu.addActionListener(new ActionListener() {
570 public void actionPerformed(ActionEvent e) {
571 toggleFollow();
572 }
573 });
574 contextMenu.add(toggleFollowMenu);
575
576 JMenuItem jumpToEndMenu = new JMenuItem("Jump To End");
577 jumpToEndMenu.addActionListener(new ActionListener() {
578 public void actionPerformed(ActionEvent e) {
579 setCaretPosition(getDocument().getLength());
580 }
581 });
582 contextMenu.add(toggleFollowMenu);
583
584 JMenuItem clearTextMenu = new JMenuItem("Clear Text");
585 clearTextMenu.addActionListener(new ActionListener() {
586 public void actionPerformed(ActionEvent e) {
587 setText("");
588 }
589 });
590 contextMenu.add(clearTextMenu);
591 return contextMenu;
592 }
593}
Note: See TracBrowser for help on using the repository browser.