/* * * * Copyright 2014 http://Bither.net * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * 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 net.bither; import net.bither.bitherj.BitherjSettings; import net.bither.bitherj.core.Address; import net.bither.bitherj.core.AddressManager; import net.bither.bitherj.crypto.mnemonic.MnemonicCode; import net.bither.db.AddressDBHelper; import net.bither.db.DesktopDbImpl; import net.bither.db.TxDBHelper; import net.bither.implbitherj.DesktopImplAbstractApp; import net.bither.logging.LoggingConfiguration; import net.bither.logging.LoggingFactory; import net.bither.mnemonic.MnemonicCodeDesktop; import net.bither.network.ReplayManager; import net.bither.platform.GenericApplication; import net.bither.platform.GenericApplicationFactory; import net.bither.platform.GenericApplicationSpecification; import net.bither.platform.builder.OSUtils; import net.bither.platform.listener.GenericOpenURIEvent; import net.bither.preference.UserPreference; import net.bither.runnable.RunnableListener; import net.bither.utils.*; import net.bither.viewsystem.CoreController; import net.bither.viewsystem.MainFrame; import net.bither.viewsystem.action.ExitAction; import net.bither.viewsystem.base.ColorAndFontConstants; import net.bither.viewsystem.base.FontSizer; import net.bither.viewsystem.dialogs.DialogConfirmTask; import net.bither.viewsystem.dialogs.DialogProgress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import javax.swing.plaf.nimbus.NimbusLookAndFeel; import javax.swing.text.DefaultEditorKit; import java.awt.*; import java.awt.event.KeyEvent; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.Locale; public final class Bither { private static final Logger log = LoggerFactory.getLogger(Bither.class); public static long reloadTxTime = -1; private static CoreController coreController = null; private static MainFrame mainFrame = null; private static GenericApplication genericApplication = null; private static ApplicationDataDirectoryLocator applicationDataDirectoryLocator = null; private static Address activeWalletModelData; /** * Utility class should not have a public constructor */ private Bither() { } @SuppressWarnings("deprecation") public static void main(String args[]) { new LoggingFactory(new LoggingConfiguration(), "bither").configure(); // LoggingFactory.bootstrap(); try { initialiseJVM(); } catch (Exception e) { e.printStackTrace(); } applicationDataDirectoryLocator = new ApplicationDataDirectoryLocator(); initBitherApplication(); initApp(args); // new Thread(new Runnable() { // @Override // public void run() { // while (true) { // StringUtil.maxUsedSize(); // try { // Thread.sleep(10); // } catch (InterruptedException e) { // e.printStackTrace(); // } // } // // } // }).start(); // System.out.println("addresses:" + AbstractDb.addressProvider.getAddresses().size()); // new Thread(new Runnable() { // @Override // public void run() { // while (true) { // AbstractDb.addressProvider.getAddresses(); // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } // StringUtil.callSystemGC(); // } // } // }).start(); } private static void initBitherApplication() { ApplicationInstanceManager.txDBHelper = new TxDBHelper(applicationDataDirectoryLocator.getApplicationDataDirectory()); ApplicationInstanceManager.txDBHelper.initDb(); ApplicationInstanceManager.addressDBHelper = new AddressDBHelper(applicationDataDirectoryLocator.getApplicationDataDirectory()); ApplicationInstanceManager.addressDBHelper.initDb(); if (UserPreference.getInstance().getAppMode() == null) { UserPreference.getInstance().setAppMode(BitherjSettings.AppMode.HOT); } DesktopImplAbstractApp desktopImplAbstractApp = new DesktopImplAbstractApp(); desktopImplAbstractApp.construct(); DesktopDbImpl desktopDb = new DesktopDbImpl(); desktopDb.construct(); AddressManager.getInstance(); try { MnemonicCode.setInstance(new MnemonicCodeDesktop()); } catch (IOException e) { e.printStackTrace(); } } public static boolean canReloadTx() { if (reloadTxTime == -1) { return true; } else { return reloadTxTime + 60 * 60 * 1000 < System.currentTimeMillis(); } } private static void runRawURI(String args[]) { for (String str : args) { System.out.println("args:" + str); } String rawURI = null; if (args != null && args.length > 0) { rawURI = args[0]; } //todo A single program if (!ApplicationInstanceManager.registerInstance(rawURI)) { // Instance already running. System.out.println("Another instance of MultiBit is already running. Exiting."); System.exit(0); } ApplicationInstanceManager.setApplicationInstanceListener(new ApplicationDataDirectoryLocator.ApplicationInstanceListener() { @Override public void newInstanceCreated(String rawURI) { final String finalRawUri = rawURI; Runnable doProcessCommandLine = new Runnable() { @Override public void run() { processCommandLineURI(finalRawUri); } }; SwingUtilities.invokeLater(doProcessCommandLine); } }); } private static void runProcessCommandLineURIWithArgs(String args[]) { log.debug("Checking for Bitcoin URI on command line"); // Check for a valid entry on the command line (protocol handler). if (args != null && args.length > 0) { for (int i = 0; i < args.length; i++) { log.debug("Started with args[{}]: '{}'", i, args[i]); } processCommandLineURI(args[0]); } else { log.debug("No Bitcoin URI provided as an argument"); } } private static void initialiseJVM() throws Exception { log.debug("Initialising JVM..."); // Although we guarantee the JVM through the packager it is possible that // a power user will use their own try { // We guarantee the JVM through the packager so we should try it first UIManager.setLookAndFeel(new NimbusLookAndFeel()); UIDefaults defaults = UIManager.getLookAndFeelDefaults(); defaults.put("nimbusOrange", defaults.get("nimbusBase")); } catch (UnsupportedLookAndFeelException e) { try { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e1) { System.exit(-1); } } // Set any bespoke system properties try { // Fix for Windows / Java 7 / VPN bug System.setProperty("java.net.preferIPv4Stack", "true"); // Fix for version.txt not visible for Java 7 System.setProperty("jsse.enableSNIExtension", "false"); if (OSUtils.isMac()) { // Ensure the correct name is displayed in the application menu System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Bither"); // Ensure OSX key bindings are used for copy, paste etc // Use the Nimbus keys and ensure this occurs before any component creation addOSXKeyStrokes((InputMap) UIManager.get("TextField.focusInputMap")); addOSXKeyStrokes((InputMap) UIManager.get("FormattedTextField.focusInputMap")); addOSXKeyStrokes((InputMap) UIManager.get("TextArea.focusInputMap")); addOSXKeyStrokes((InputMap) UIManager.get("PasswordField.focusInputMap")); addOSXKeyStrokes((InputMap) UIManager.get("EditorPane.focusInputMap")); } } catch (SecurityException se) { log.error(se.getClass().getName() + " " + se.getMessage()); } } private static void addOSXKeyStrokes(InputMap inputMap) { // Undo and redo require more complex handling inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.META_DOWN_MASK), DefaultEditorKit.copyAction); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.META_DOWN_MASK), DefaultEditorKit.cutAction); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK), DefaultEditorKit.pasteAction); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.META_DOWN_MASK), DefaultEditorKit.selectAllAction); } private static void fixJavaBug() { // Set any bespoke system properties. try { // Fix for Windows / Java 7 / VPN bug. System.setProperty("java.net.preferIPv4Stack", "true"); // Fix for version.txt not visible for Java 7 System.setProperty("jsse.enableSNIExtension", "false"); } catch (SecurityException se) { log.error(se.getClass().getName() + " " + se.getMessage()); } } private static void initController(String[] args) { try { coreController = new CoreController(); GenericApplicationSpecification specification = new GenericApplicationSpecification(); specification.getOpenURIEventListeners().add(coreController); specification.getPreferencesEventListeners().add(coreController); specification.getAboutEventListeners().add(coreController); specification.getQuitEventListeners().add(coreController); genericApplication = GenericApplicationFactory.INSTANCE.buildGenericApplication(specification); runRawURI(args); Localiser localiser; String userLanguageCode = UserPreference.getInstance().getUserLanguageCode(); if (userLanguageCode == null) { // Initial install - no language info supplied - see if we can // use the user default, else Localiser will set it to English. localiser = new Localiser(Locale.getDefault()); UserPreference.getInstance().setUserLanguageCode(localiser.getLocale().getLanguage()); } else { if (BitherSetting.USER_LANGUAGE_IS_DEFAULT.equals(userLanguageCode)) { localiser = new Localiser(Locale.getDefault()); } else { localiser = new Localiser(new Locale(userLanguageCode)); } } LocaliserUtils.setLocaliser(localiser); // Initialise replay manager. ReplayManager.INSTANCE.initialise(false); // Initialise singletons. ColorAndFontConstants.init(); FontSizer.INSTANCE.initialise(); mainFrame = new MainFrame(coreController, coreController.getCurrentView()); coreController.registerViewSystem(mainFrame); runProcessCommandLineURIWithArgs(args); // Indicate to the application that startup has completed. coreController.setApplicationStarting(false); // Check for any pending URI operations. // bitcoinController.handleOpenURI(rememberedRawBitcoinURI); // Check to see if there is a new version. } catch (Exception e) { // An odd unrecoverable error occurred. e.printStackTrace(); e.printStackTrace(); // Try saving any dirty wallets. if (coreController != null) { ExitAction exitAction = new ExitAction(); exitAction.actionPerformed(null); } } setVersionCode(); } private static void setVersionCode() { if (UpgradeUtil.needUpgrade()) { final DialogProgress dialogProgress = new DialogProgress(); UpgradeUtil.upgradeNewVerion(new RunnableListener() { @Override public void prepare() { PeerUtil.stopPeer(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { dialogProgress.pack(); dialogProgress.setVisible(true); } }); } @Override public void success(Object obj) { PeerUtil.startPeer(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { dialogProgress.dispose(); Bither.refreshFrame(); UserPreference.getInstance().setVerionCode(BitherSetting.VERSION_CODE); PeerUtil.startPeer(); } }); } @Override public void error(int errorCode, String errorMsg) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { dialogProgress.dispose(); DialogConfirmTask dialogConfirmTask = new DialogConfirmTask(LocaliserUtils.getString("upgrade_error_db_is_lock"), null); dialogConfirmTask.pack(); dialogConfirmTask.setVisible(true); ExitAction exitAction = new ExitAction(); exitAction.actionPerformed(null); } }); } }); } else { if (UserPreference.getInstance().getVerionCode() < BitherSetting.VERSION_CODE) { UserPreference.getInstance().setVerionCode(BitherSetting.VERSION_CODE); } PeerUtil.startPeer(); } } private static void initApp(final String args[]) { // Enclosing try to enable graceful closure for unexpected errors. fixJavaBug(); if (SwingUtilities.isEventDispatchThread()) { initController(args); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { initController(args); // Create the controllers. } }); } } private static void processCommandLineURI(String rawURI) { try { // Attempt to detect if the command line URI is valid. // Note that this is largely because IE6-8 strip URL encoding // when passing in URIs to a protocol handler. // However, there is also the chance that anyone could // hand-craft a URI and pass // it in with non-ASCII character encoding present in the label // This a really limited approach (no consideration of // "amount=10.0&label=Black & White") // but should be OK for early use cases. int queryParamIndex = rawURI.indexOf('?'); if (queryParamIndex > 0 && !rawURI.contains("%")) { // Possibly encoded but more likely not String encodedQueryParams = URLEncoder.encode(rawURI.substring(queryParamIndex + 1), "UTF-8"); rawURI = rawURI.substring(0, queryParamIndex) + "?" + encodedQueryParams; rawURI = rawURI.replaceAll("%3D", "="); rawURI = rawURI.replaceAll("%26", "&"); } final URI uri; log.debug("Working with '{}' as a Bitcoin URI", rawURI); // Construct an OpenURIEvent to simulate receiving this from a // listener uri = new URI(rawURI); GenericOpenURIEvent event = new GenericOpenURIEvent() { @Override public URI getURI() { return uri; } }; Bither.getCoreController().displayView(Bither.getCoreController().getCurrentView()); // Call the event which will attempt validation against the // Bitcoin URI specification. coreController.onOpenURIEvent(event); } catch (URISyntaxException e) { log.error("URI is malformed. Received: '{}'", rawURI); } catch (UnsupportedEncodingException e) { log.error("UTF=8 is not supported on this platform"); } } public static MainFrame getMainFrame() { return mainFrame; } public static CoreController getCoreController() { return coreController; } public static GenericApplication getGenericApplication() { return genericApplication; } public static ApplicationDataDirectoryLocator getApplicationDataDirectoryLocator() { return applicationDataDirectoryLocator; } public static Address getActionAddress() { return activeWalletModelData; } public static void setActivePerWalletModelData(Address address) { activeWalletModelData = address; } public static void refreshFrame() { if (SwingUtilities.isEventDispatchThread()) { refreshFrameInUi(); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { refreshFrameInUi(); } }); } } private static void refreshFrameInUi() { Bither.getMainFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); Bither.getCoreController().fireRecreateAllViews(true); Bither.getCoreController().fireDataChangedUpdateNow(); Bither.getMainFrame().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Bither.getMainFrame().getMainFrameUi().clearScroll(); } }); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } }