/* * FrontlineSMS <http://www.frontlinesms.com> * Copyright 2007, 2008 kiwanja * * This file is part of FrontlineSMS. * * FrontlineSMS is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * FrontlineSMS is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with FrontlineSMS. If not, see <http://www.gnu.org/licenses/>. */ package net.frontlinesms; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import javax.swing.UIManager; import net.frontlinesms.encoding.Base64Utils; import net.frontlinesms.resources.ResourceUtils; import net.frontlinesms.ui.FirstTimeWizard; import net.frontlinesms.ui.UiGeneratorController; import net.frontlinesms.ui.handler.core.DatabaseConnectionFailedDialog; import net.frontlinesms.ui.i18n.InternationalisationUtils; import net.frontlinesms.ui.i18n.LanguageBundle; import org.apache.log4j.Logger; import thinlet.Thinlet; /** * This class is the Launcher for FrontlineSMS as a desktop application. It will * start the Frontline service and then open the graphical user interface to * control the service. If any unhandled exceptions occur either starting the * FrontlineSMS service or opening the GUI, they will be diaplyed in an AWT window * so that they can easily be reported back to the development team. * N.B. Error messages CANNOT be i18ned in this class, as there may have been an error * loading language packs. * * @author Alex Anderson alex(at)masabi(dot)com * @author Carlos Eduardo Genz kadu(at)masabi(dot)com */ public class DesktopLauncher { /** Logging object */ private static final Logger LOG = FrontlineUtils.getLogger(DesktopLauncher.class); /** * Main class for launching the FrontlineSMS project. * @param args */ public static void main(String[] args) { FrontlineSMS frontline = null; try { AppProperties appProperties = AppProperties.getInstance(); final String VERSION = BuildProperties.getInstance().getVersion(); LOG.info("FrontlineSMS version [" + VERSION + "]"); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); String lastVersion = appProperties.getLastRunVersion(); InputStream defaultResourceArchive = ResourceUtils.class.getResourceAsStream("/resources.zip"); if(defaultResourceArchive == null) { LOG.fatal("Default resources archive could not be found!"); throw new Exception("Default resources archive could not be found!"); } ResourceUtils.unzip(defaultResourceArchive, new File(ResourceUtils.getConfigDirectoryPath()), !VERSION.equals(lastVersion)); // This should always get the English bundle, as other languages are only included in // resources.zip rather than in the resources/languages directory LanguageBundle englishBundle = InternationalisationUtils.getDefaultLanguageBundle(); Thinlet.DEFAULT_ENGLISH_BUNDLE = englishBundle.getProperties(); // If the user has currently no User ID defined // We generate one if (appProperties.getUserId() == null) { appProperties.setUserId(generateUserId()); } boolean showWizard = appProperties.isShowWizard(); appProperties.setLastRunVersion(VERSION); appProperties.saveToDisk(); frontline = initFrontline(); if (showWizard) { new FirstTimeWizard(frontline); } else { // Auto-detect phones. new UiGeneratorController(frontline, true); } } catch(Throwable t) { if (frontline != null) frontline.destroy(); // Rather than swallowing the error, we now display it to the user // so that they can give us some feedback :) ErrorUtils.showErrorDialog("Fatal error starting FrontlineSMS!", "A problem ocurred during FrontlineSMS startup.", t, true); } } /** * Generate the User ID this user is going to keep for all its statistics * @return The generated ID as a String */ private static String generateUserId() { Long currentTime = new Long(System.currentTimeMillis()); byte[] bytes; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); new DataOutputStream(baos).writeLong(currentTime); bytes = baos.toByteArray(); } catch (IOException e) { /* not gonna happen */ throw new IllegalStateException(e); } return Base64Utils.encode(bytes).replace('=', ' ').trim(); } private static FrontlineSMS initFrontline() throws Throwable { LOG.trace("ENTER"); FrontlineSMS frontline = new FrontlineSMS(); try { // Test the database connection is working boolean connected = false; while(!connected) { try { frontline.initApplicationContext(); frontline.getContactDao().getContactByName("test"); connected = true; } catch(RuntimeException ex) { LOG.warn("Problem initialising application context.", ex); frontline.deinitApplicationContext(); DatabaseConnectionFailedDialog.create(ex).acquireSettings(); } } // Start FrontlineSMS services frontline.startServices(); LOG.trace("EXIT"); return frontline; } catch(Throwable t) { LOG.info("Problem initialising FrontlineSMS", t); // This try {} catch {} is necessary to make sure the ThinletWorker thread // is shut down when an exception is thrown. An alternative would be to // explicitly START the worker when we know this constructor has successfully // completed. frontline.destroy(); LOG.trace("EXIT"); throw t; } } }