/**
* Copyright 2011 multibit.org
*
* Licensed under the MIT license (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at
*
* http://opensource.org/licenses/mit-license.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.multibit.viewsystem.swing.action;
import com.google.bitcoin.store.BlockStore;
import com.google.bitcoin.store.BlockStoreException;
import com.google.common.util.concurrent.Uninterruptibles;
import org.multibit.ApplicationInstanceManager;
import org.multibit.controller.Controller;
import org.multibit.controller.bitcoin.BitcoinController;
import org.multibit.controller.core.CoreController;
import org.multibit.file.BackupManager;
import org.multibit.file.FileHandler;
import org.multibit.file.WalletSaveException;
import org.multibit.message.Message;
import org.multibit.message.MessageManager;
import org.multibit.model.bitcoin.WalletData;
import org.multibit.store.WalletVersionException;
import org.multibit.viewsystem.swing.HealthCheckTimerTask;
import org.multibit.viewsystem.swing.MultiBitFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Exit the application.
*/
public class ExitAction extends AbstractExitAction {
private static final long serialVersionUID = 8784284740245520863L;
private static final int MAXIMUM_TIME_TO_WAIT_FOR_HEALTH_CHECK_TASK = 30000; // ms
private static final int TIME_TO_WAIT = 200; // ms
private final MultiBitFrame mainFrame;
private static final Logger log = LoggerFactory.getLogger(ExitAction.class);
private CoreController coreController = null;
private BitcoinController bitcoinController = null;
/**
* Boolean indicating if the ExitAction is running.
* This depends on there only being one ExitAction being active at any one time
*/
private static boolean running = false;
/**
* Creates a new {@link ExitAction}.
*/
public ExitAction(Controller controller, MultiBitFrame mainFrame) {
super(controller);
this.mainFrame = mainFrame;
}
public void setCoreController(CoreController coreController) {
if (null == coreController) {
this.coreController = coreController;
}
}
public void setBitcoinController(BitcoinController bitcoinController) {
if (null == this.bitcoinController) {
this.bitcoinController = bitcoinController;
}
}
@Override
public void actionPerformed(ActionEvent arg0) {
running = true;
String shuttingDownTitle = bitcoinController.getLocaliser().getString("multiBitFrame.title.shuttingDown");
if (mainFrame != null) {
mainFrame.setTitle(shuttingDownTitle);
if (EventQueue.isDispatchThread()) {
mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
});
}
// If the HealthCheckTimerTask is running wait until it completes.
HealthCheckTimerTask healthCheckTimerTask = mainFrame.getHealthCheckTimerTask();
if (healthCheckTimerTask != null) {
int timeWaited = 0;
while(healthCheckTimerTask.isRunning() && timeWaited < MAXIMUM_TIME_TO_WAIT_FOR_HEALTH_CHECK_TASK) {
log.debug("Waiting for healthCheckTimerTask to complete (waited so far = " + timeWaited + "). . .");
Uninterruptibles.sleepUninterruptibly(TIME_TO_WAIT, TimeUnit.MILLISECONDS);
timeWaited = timeWaited + TIME_TO_WAIT;
}
}
}
if (bitcoinController != null && bitcoinController.getMultiBitService() != null) {
// Stop the peer group so that blocks are notified to wallets correctly.
if (bitcoinController.getMultiBitService().getPeerGroup() != null) {
log.debug("Closing Bitcoin network connection...");
bitcoinController.getMultiBitService().getPeerGroup().stopAndWait();
log.debug("PeerGroup is now stopped.");
}
// Close down the blockstore.
BlockStore blockStore = bitcoinController.getMultiBitService().getBlockStore();
if (blockStore != null) {
try {
log.debug("Closing blockStore. . .");
blockStore.close();
blockStore = null;
log.debug("BlockStore closed successfully.");
} catch (NullPointerException npe) {
log.error("NullPointerException on blockstore close");
} catch (BlockStoreException e) {
log.error("BlockStoreException on blockstore close. Message was '" + e.getMessage() + "'");
}
}
}
if (bitcoinController != null) {
// Save all the wallets and put their filenames in the user preferences.
List<WalletData> perWalletModelDataList = bitcoinController.getModel().getPerWalletModelDataList();
if (perWalletModelDataList != null) {
for (WalletData loopPerWalletModelData : perWalletModelDataList) {
try {
String titleText = shuttingDownTitle;
if (mainFrame != null) {
if (loopPerWalletModelData != null) {
titleText = bitcoinController.getLocaliser().getString("multiBitFrame.title.saving",
new String[] { loopPerWalletModelData.getWalletDescription() });
}
if (EventQueue.isDispatchThread()) {
mainFrame.setTitle(titleText);
} else {
final String finalTitleText = titleText;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
mainFrame.setTitle(finalTitleText);
}
});
}
}
bitcoinController.getFileHandler().savePerWalletModelData(loopPerWalletModelData, false);
} catch (WalletSaveException wse) {
log.error(wse.getClass().getCanonicalName() + " " + wse.getMessage());
MessageManager.INSTANCE.addMessage(new Message(wse.getClass().getCanonicalName() + " " + wse.getMessage()));
// Save to backup.
try {
BackupManager.INSTANCE.backupPerWalletModelData(bitcoinController.getFileHandler(), loopPerWalletModelData);
} catch (WalletSaveException wse2) {
log.error(wse2.getClass().getCanonicalName() + " " + wse2.getMessage());
MessageManager.INSTANCE.addMessage(new Message(wse2.getClass().getCanonicalName() + " "
+ wse2.getMessage()));
}
} catch (WalletVersionException wve) {
log.error(wve.getClass().getCanonicalName() + " " + wve.getMessage());
MessageManager.INSTANCE.addMessage(new Message(wve.getClass().getCanonicalName() + " " + wve.getMessage()));
}
}
}
// Write the user properties.
log.debug("Saving user preferences ...");
FileHandler.writeUserPreferences(bitcoinController);
}
log.debug("Shutting down Bitcoin URI checker ...");
ApplicationInstanceManager.shutdownSocket();
// Get rid of main display.
if (mainFrame != null) {
mainFrame.setVisible(false);
}
if (mainFrame != null) {
mainFrame.dispose();
}
running = false;
System.exit(0);
}
public static boolean isRunning() {
return running;
}
}