/* * Created on 8 juil. 2003 * Copyright (C) 2003, 2004, 2005, 2006 Aelitis, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * AELITIS, SAS au capital de 46,603.30 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package org.gudy.azureus2.ui.swt; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.logging.*; import org.gudy.azureus2.core3.util.*; import com.aelitis.azureus.core.AzureusCore; import com.aelitis.azureus.core.AzureusCoreException; import com.aelitis.azureus.core.AzureusCoreFactory; import com.aelitis.azureus.launcher.Launcher; /** * @author Olivier * */ public class Main { private static final LogIDs LOGID = LogIDs.GUI; public static final String PR_MULTI_INSTANCE = "MULTI_INSTANCE"; // values "true" or "false" StartServer startServer; public static long startTime = System.currentTimeMillis(); // This method is called by other Main classes via reflection - must be kept public. public Main(String args[]) { try{ // this should not be necessary, but since it's public let's play safe if(Launcher.checkAndLaunch(Main.class, args)) return; // This *has* to be done first as it sets system properties that are read and cached by Java COConfigurationManager.preInitialise(); Constructor constructor = null; try { Class az3Class = Class.forName("com.aelitis.azureus.ui.swt.Initializer"); constructor = az3Class.getConstructor(new Class[] { AzureusCore.class, StartServer.class, String[].class }); } catch (ClassNotFoundException cnfe) { System.err.println(cnfe.toString() + "\nDid you include the azureus3 module?"); return; } catch (Throwable t) { t.printStackTrace(); return; } String mi_str = System.getProperty( PR_MULTI_INSTANCE ); boolean mi = mi_str != null && mi_str.equalsIgnoreCase("true"); startServer = new StartServer(); boolean debugGUI = Boolean.getBoolean("debug"); if( mi || debugGUI){ // create a MainWindow regardless to the server state AzureusCore core = AzureusCoreFactory.create(); constructor.newInstance(new Object[] { core, startServer, args }); return; } if ( processParams(args, startServer) ){ AzureusCore core = AzureusCoreFactory.create(); startServer.pollForConnections(core); constructor.newInstance(new Object[] { core, startServer, args }); } }catch( AzureusCoreException e ){ Logger.log(new LogEvent(LOGID, "Start failed", e)); }catch( Throwable t ){ t.printStackTrace(); } } /** * @param args * @return whether to init the core */ public static boolean processParams(String[] args, StartServer startServer) { boolean closedown = false; boolean another_instance = startServer.getState() != StartServer.STATE_LISTENING; /* if another instance is running then set the property which is checked during * class instantiation by various stuff to to avoid pulling in too much state * from the already running instance */ if(another_instance) System.setProperty("transitory.startup", "1"); // WATCH OUT FOR LOGGING HERE - we don't want to use Logger if this is a secondary instance as // it initialised TOO MUCH of AZ core for (int i=0;i<args.length;i++){ String arg = args[i]; if ( arg.equalsIgnoreCase( "--closedown" ) || arg.equalsIgnoreCase( "--shutdown" ) || arg.equalsIgnoreCase( "--restart" ) ){ closedown = true; break; } // Sometimes Windows use filename in 8.3 form and cannot // match .torrent extension. To solve this, canonical path // is used to get back the long form String filename = arg; if ( filename.length() == 40 ){ byte[] hash = null; try{ hash = ByteFormatter.decodeString( filename ); }catch( Throwable e ){ } if ( hash != null && hash.length == 20 ){ filename = "magnet:?xt=urn:btih:" + Base32.encode( hash ); } } // handle base32 info hash if ( filename.length() == 32 ){ byte[] hash = null; try{ hash = Base32.decode( filename ); }catch( Throwable e ){ } if ( hash != null && hash.length == 20 ){ filename = "magnet:?xt=urn:btih:" +filename; } } if( filename.toUpperCase().startsWith( "HTTP:" ) || filename.toUpperCase().startsWith( "HTTPS:" ) || filename.toUpperCase().startsWith( "MAGNET:" ) || filename.toUpperCase().startsWith( "BC:" ) || filename.toUpperCase().startsWith( "BCTP:" ) || filename.toUpperCase().startsWith( "DHT:" ) ) { if ( !another_instance ){ Logger.log(new LogEvent(LOGID, "Main::main: args[" + i + "] handling as a URI: " + filename)); } continue; //URIs cannot be checked as a .torrent file } try{ File file = new File(filename); if ( !file.exists()){ throw( new Exception("File '" + file + "' not found" )); } args[i] = file.getCanonicalPath(); // don't use logger if we're not the main instance as we don't want all // the associated core initialisation + debug file moving... if ( (!another_instance) && Logger.isEnabled()){ Logger.log(new LogEvent(LOGID, "Main::main: args[" + i + "] exists = " + new File(filename).exists())); } }catch( Throwable e ){ if ( another_instance ){ e.printStackTrace(); }else{ Logger.log(new LogAlert(LogAlert.REPEATABLE, LogAlert.AT_ERROR, "Failed to access torrent file '" + filename + "'. Ensure sufficient temporary " + "file space available (check browser cache usage).")); } } } if( another_instance ) { //looks like there's already a process listening on 127.0.0.1:<port> //attempt to pass args to existing instance // First, do some OSX magic because parameters are passed via OpenDocument API and other callbacks args = CocoaMagic(args); StartSocket ss = new StartSocket(args); if( !ss.sendArgs() ) { //arg passing attempt failed, so start core anyway another_instance = false; String msg = "There appears to be another program process already listening on socket [127.0.0.1: "+Constants.INSTANCE_PORT+"].\nLoading of torrents via command line parameter will fail until this is fixed."; System.out.println( msg ); Logger.log(new LogAlert(LogAlert.REPEATABLE, LogAlert.AT_WARNING, msg)); } } if ( !another_instance ){ if ( closedown ){ // closedown request and no instance running return false; } return true; } return false; } private static String[] CocoaMagic(String[] args) { if (!Constants.isOSX) { return args; } try { // hack to tell OSXAccess static initializer to only initilize a light version System.setProperty("osxaccess.light", "1"); Class<?> claOSXAccess = Class.forName("org.gudy.azureus2.platform.macosx.access.jnilib.OSXAccess"); if (claOSXAccess != null) { Method method = claOSXAccess.getMethod("runLight", new Class[] { String[].class }); Object invoke = method.invoke(null, new Object[] { args }); return (String[]) invoke; } } catch (Throwable e) { Debug.printStackTrace(e); } return args; } public static void main(String args[]) { if(Launcher.checkAndLaunch(Main.class, args)) return; //Debug.dumpThreads("Entry threads"); //Debug.dumpSystemProperties(); if (System.getProperty("ui.temp") == null) { System.setProperty("ui.temp", "az2"); } new Main(args); } }