1 /**
2  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  *
16  */
17 
18 package org.gudy.azureus2.ui.common;
19 
20 import java.io.FileReader;
21 import java.io.OutputStreamWriter;
22 import java.io.PrintStream;
23 import java.io.PrintWriter;
24 import java.io.Reader;
25 import java.io.StringReader;
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.Method;
28 import java.net.Socket;
29 import java.text.SimpleDateFormat;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.StringTokenizer;
34 
35 import org.apache.commons.cli.CommandLine;
36 import org.apache.commons.cli.CommandLineParser;
37 import org.apache.commons.cli.HelpFormatter;
38 import org.apache.commons.cli.OptionBuilder;
39 import org.apache.commons.cli.Options;
40 import org.apache.commons.cli.ParseException;
41 import org.apache.commons.cli.PosixParser;
42 import org.apache.log4j.Appender;
43 import org.apache.log4j.ConsoleAppender;
44 import org.apache.log4j.Logger;
45 import org.apache.log4j.PatternLayout;
46 import org.apache.log4j.varia.DenyAllFilter;
47 
48 import com.aelitis.azureus.core.*;
49 import com.aelitis.azureus.core.impl.AzureusCoreSingleInstanceClient;
50 import com.aelitis.azureus.launcher.Launcher;
51 
52 import org.gudy.azureus2.core3.config.COConfigurationManager;
53 import org.gudy.azureus2.core3.util.Constants;
54 import org.gudy.azureus2.ui.common.IUserInterface;
55 import org.gudy.azureus2.ui.common.UserInterfaceFactory;
56 /**
57  *
58  * @author  Tobias Minich
59  */
60 public class Main {
61 
62   public static String DEFAULT_UI = "swt";
63 
64   public static StartServer start = null;
65 
66   protected static AzureusCore	core;
67 
parseCommands(String[] args, boolean constart)68   private static CommandLine parseCommands(String[] args, boolean constart) {
69 
70     if (args==null)
71       return null;
72 
73     CommandLineParser parser = new PosixParser();
74     Options options = new Options();
75     options.addOption("h", "help", false, "Show this help.");
76 
77     OptionBuilder.withLongOpt("exec");
78     OptionBuilder.hasArg();
79     OptionBuilder.withArgName("file");
80     OptionBuilder.withDescription("Execute script file. The file should end with 'logout', otherwise the parser thread doesn't stop.");
81     options.addOption( OptionBuilder.create('e'));
82 
83     OptionBuilder.withLongOpt("command");
84     OptionBuilder.hasArg();
85     OptionBuilder.withArgName("command");
86     OptionBuilder.withDescription("Execute single script command. Try '-c help' for help on commands.");
87     options.addOption(OptionBuilder.create('c'));
88 
89     OptionBuilder.withLongOpt("ui");
90     OptionBuilder.withDescription("Run <uis>. ',' separated list of user interfaces to run. The first one given will respond to requests without determinable source UI (e.g. further torrents added via command line).");
91     OptionBuilder.withArgName("uis");
92     OptionBuilder.hasArg();
93     options.addOption(OptionBuilder.create('u'));
94 
95     CommandLine commands = null;
96     try {
97       commands = parser.parse(options, args, true);
98     } catch( ParseException exp ) {
99       Logger.getLogger("azureus2").error("Parsing failed.  Reason: " + exp.getMessage(), exp);
100       if (constart)
101         System.exit(2);
102     }
103     if ( commands != null && commands.hasOption('h')) {
104       if (constart) {
105         HelpFormatter hf = new HelpFormatter();
106         hf.printHelp("java org.gudy.azureus2.ui.common.Main", "Optionally you can put torrent files to add to the end of the command line.\r\n", options, "Available User Interfaces: swt (default), web, console\r\nThe default interface is not started if you give either the '-e' or '-c' option (But you can start it by hand with '-u').", true);
107         System.exit(0);
108       }
109     }
110     return commands;
111   }
112 
initRootLogger()113   public static void initRootLogger() {
114     if (Logger.getRootLogger().getAppender("ConsoleAppender")==null) {
115       Appender app;
116       app = new ConsoleAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN));
117       app.setName("ConsoleAppender");
118       app.addFilter( new DenyAllFilter() );  //'log off' by default
119       Logger.getRootLogger().addAppender(app);
120     }
121   }
122 
main(String[] args)123   public static void main(String[] args) {
124 	  if(Launcher.checkAndLaunch(Main.class, args))
125 		  return;
126 
127 		// This *has* to be done first as it sets system properties that are read and cached by Java
128 
129 	COConfigurationManager.preInitialise();
130 
131     String  mi_str = System.getProperty( "MULTI_INSTANCE" );
132 
133     boolean mi = mi_str != null && mi_str.equalsIgnoreCase("true");
134 
135     initRootLogger();
136 
137     try{
138        	CommandLine commands = parseCommands(args, true);
139 
140        	if ( commands != null && directLaunch( args, commands )){
141 
142        		return;
143        	}
144 
145        		// don't create core until we know we really need it
146 
147     	if( mi ){
148 
149     		System.out.println( "MULTI_INSTANCE enabled" );
150 
151     	   	core = AzureusCoreFactory.create();
152 
153     		processArgs(args, core, commands);
154 
155     		return;
156     	}
157 
158     	start = new StartServer();
159 
160 	    if ((start == null) || (start.getServerState()==StartServer.STATE_FAULTY)) {
161 
162 
163 	    	new StartSocket( args );
164 
165 	    }else{
166 
167 
168 	      core = AzureusCoreFactory.create();
169 
170 
171 	      start.start();
172 
173 	      processArgs(args, core, commands);
174 	    }
175     }catch( AzureusCoreException e ){
176 
177     	System.out.println( "Start fails:" );
178 
179     	e.printStackTrace();
180     }
181   }
182 
shutdown()183   public static void shutdown() {
184     if (start!=null){
185 
186       start.stopIt();
187     }
188 
189     if ( core != null ){
190     	try{
191     		core.stop();
192 
193     	}catch( AzureusCoreException e ){
194 
195     		System.out.println( "Stop fails:" );
196 
197     		e.printStackTrace();
198     	}
199     }
200 
201     SimpleDateFormat temp = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
202     Logger.getLogger("azureus2").fatal("Azureus stopped at "+temp.format(new Date()));
203     //System.exit(0);	- we don't want to force quit, wait until other threads have completed
204     // so that resume data etc is saved....
205   }
206 
207   	public static boolean
directLaunch( String[] args, CommandLine commands)208   	directLaunch(
209   		String[] 		args,
210   		CommandLine 	commands)
211   	{
212   			// frig to support launch of SWT ui via this means pending a proper rewrite
213   			// of this stuff
214 
215   		if ( commands.hasOption('u')) {
216 
217   			String uinames = commands.getOptionValue('u');
218 
219   			if ( uinames.indexOf(',') != -1 ){
220 
221   				return( false );
222   			}
223 
224   			if ( !uinames.equalsIgnoreCase( DEFAULT_UI )){
225 
226   				return( false );
227   			}
228   		}
229 
230   		try{
231   			String uiclass = "org.gudy.azureus2.ui." + DEFAULT_UI + ".Main";
232 
233   			Class	main_class = Class.forName( uiclass );
234 
235   			Method main_method = main_class.getMethod( "main", new Class[]{ String[].class });
236 
237    			main_method.invoke( null, new Object[]{ commands.getArgs()});
238 
239   			return( true );
240 
241   		}catch( Throwable e ){
242 
243   			e.printStackTrace();
244 
245   			return( false );
246   		}
247   }
248 
249   public static void
processArgs( String[] args, AzureusCore new_core, CommandLine commands)250   processArgs(
251   	String[] 		args,
252   	AzureusCore 	new_core,
253 	CommandLine 	commands)
254   {
255     if (commands==null) {
256       commands = parseCommands(args, false);
257     }
258     if (commands!=null && ( args.length > 0 || new_core != null)) {
259       if (UIConst.UIS == null) {
260         UIConst.UIS = new HashMap();
261       }
262       if (commands.hasOption('u')) {
263         String uinames = commands.getOptionValue('u');
264         if (uinames.indexOf(',')==-1) {
265           if (!UIConst.UIS.containsKey(uinames))
266           UIConst.UIS.put(uinames,UserInterfaceFactory.getUI(uinames));
267         } else {
268           StringTokenizer stok = new StringTokenizer(uinames, ",");
269           while (stok.hasMoreTokens()) {
270             String uin = stok.nextToken();
271             if (!UIConst.UIS.containsKey(uin))
272               UIConst.UIS.put(uin,UserInterfaceFactory.getUI(uin));
273           }
274         }
275       } else {
276         if (UIConst.UIS.isEmpty() && !commands.hasOption('c') && !commands.hasOption('e'))
277           UIConst.UIS.put(DEFAULT_UI, UserInterfaceFactory.getUI(DEFAULT_UI));
278       }
279 
280       Iterator uis = UIConst.UIS.values().iterator();
281       boolean isFirst = true;
282       String [] theRest = commands.getArgs();
283       while (uis.hasNext()) {
284         IUserInterface ui = (IUserInterface) uis.next();
285         ui.init(isFirst, (UIConst.UIS.size()>1));
286         theRest = ui.processArgs(theRest);
287         isFirst = false;
288       }
289 
290       if ( new_core != null ){
291 
292         SimpleDateFormat temp = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
293 
294         UIConst.startTime = new Date();
295 
296         Logger.getLogger("azureus2").fatal("Azureus started at "+temp.format(UIConst.startTime));
297 
298         UIConst.setAzureusCore( new_core );
299       }
300 
301       uis = UIConst.UIS.values().iterator();
302       while (uis.hasNext())
303         ((IUserInterface) uis.next()).startUI();
304 
305       Class clConsoleInput;
306       Constructor conConsoleInput =null;
307       try {
308       	clConsoleInput = Class.forName("org.gudy.azureus2.ui.console.ConsoleInput");
309 
310       		// change this and you'll need to change the parameters below....
311 
312       	Class params[] = {String.class, AzureusCore.class, Reader.class, PrintStream.class, Boolean.class};
313 
314       	conConsoleInput=clConsoleInput.getConstructor(params);
315       } catch (Exception e) {
316       	e.printStackTrace();
317       }
318       if (commands.hasOption('e')) {
319       	if (conConsoleInput != null) {
320 	        try {
321 	        	Object params[] = {commands.getOptionValue('e'), new_core, new FileReader(commands.getOptionValue('e')), System.out, Boolean.FALSE};
322 	        	conConsoleInput.newInstance(params);
323 	        } catch (java.io.FileNotFoundException e) {
324 	          Logger.getLogger("azureus2").error("Script file not found: "+e.toString());
325 	        } catch (Exception e) {
326 	        	Logger.getLogger("azureus2").error("Error invocating the script processor: "+e.toString());
327 	        }
328       	} else
329       		Logger.getLogger("azureus2").error("ConsoleInput class not found. You need the console ui package to use '-e'");
330       }
331 
332       if (commands.hasOption('c')) {
333       	if (conConsoleInput != null) {
334 	        String comm = commands.getOptionValue('c');
335 	        comm+="\nlogout\n";
336 	        Object params[] = {commands.getOptionValue('c'), UIConst.getAzureusCore(), new StringReader(comm), System.out, Boolean.FALSE};
337 	        try {
338 	        	conConsoleInput.newInstance(params);
339 	        } catch (Exception e) {
340 	        	Logger.getLogger("azureus2").error("Error invocating the script processor: "+e.toString());
341 	        }
342       	} else
343       		Logger.getLogger("azureus2").error("ConsoleInput class not found. You need the console ui package to use '-e'");
344       }
345 
346       openTorrents(theRest);
347     } else {
348       Logger.getLogger("azureus2").error("No commands to process");
349     }
350   }
351 
openTorrents(String[] torrents)352   public static void openTorrents(String[] torrents) {
353     if ((UIConst.UIS!=null) && (!UIConst.UIS.isEmpty()) && (torrents.length>0)) {
354       for(int l=0; l<torrents.length; l++) {
355         ((IUserInterface) UIConst.UIS.values().toArray()[0]).openTorrent(torrents[l]);
356       }
357     }
358   }
359 
360   public static class StartSocket {
StartSocket(String args[])361     public StartSocket(String args[]) {
362       Socket sck = null;
363       PrintWriter pw = null;
364       try {
365         System.out.println("StartSocket: passing startup args to already-running process.");
366 
367 		// NOTE - this formatting is also used by AzureusCoreSingleInstanceClient and other org.gudy.azureus2.ui.swt.StartSocket
368 
369         sck = new Socket("127.0.0.1", Constants.INSTANCE_PORT );
370         pw = new PrintWriter(new OutputStreamWriter(sck.getOutputStream()));
371         StringBuffer buffer = new StringBuffer(AzureusCoreSingleInstanceClient.ACCESS_STRING+";args;");
372         for(int i = 0 ; i < args.length ; i++) {
373           String arg = args[i].replaceAll("&","&&").replaceAll(";","&;");
374           buffer.append(arg);
375           buffer.append(';');
376         }
377         pw.println(buffer.toString());
378         pw.flush();
379       } catch(Exception e) {
380         e.printStackTrace();
381       } finally {
382         try {
383           if (pw != null)
384             pw.close();
385         } catch (Exception e) {
386         }
387         try {
388           if (sck != null)
389             sck.close();
390         } catch (Exception e) {
391         }
392       }
393     }
394   }
395 }
396