View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.plugin.overnet;
21  
22  import java.awt.event.KeyEvent;
23  import java.io.File;
24  import java.io.IOException;
25  import java.util.List;
26  import javax.help.HelpSet;
27  import javax.swing.Action;
28  import javax.swing.Icon;
29  import javax.swing.JComponent;
30  import javax.swing.JMenu;
31  import org.apache.log4j.Logger;
32  import org.xnap.XNap;
33  import org.xnap.XNapFacade;
34  import org.xnap.action.AbstractXNapAction;
35  import org.xnap.event.StateEvent;
36  import org.xnap.event.StateListener;
37  import org.xnap.gui.AbstractPreferencesDialog;
38  import org.xnap.gui.DefaultPrefsWizardDialog;
39  import org.xnap.gui.StatusBar;
40  import org.xnap.gui.XNapFrame;
41  import org.xnap.gui.action.FocusAction;
42  import org.xnap.gui.component.XNapMenu;
43  import org.xnap.gui.component.XNapMenuItem;
44  import org.xnap.gui.event.AbstractPreferencesDialogListener;
45  import org.xnap.gui.shortcut.ActionShortcut;
46  import org.xnap.gui.shortcut.ShortcutManager;
47  import org.xnap.gui.util.GUIHelper;
48  import org.xnap.gui.util.IconHelper;
49  import org.xnap.plugin.AbstractPlugin;
50  import org.xnap.plugin.overnet.gui.OvernetPanel;
51  import org.xnap.plugin.overnet.gui.OvernetPreferencesPanel;
52  import org.xnap.plugin.overnet.gui.OvernetStatusPanel;
53  import org.xnap.plugin.overnet.gui.action.ConnectAction;
54  import org.xnap.plugin.overnet.gui.action.DisconnectAction;
55  import org.xnap.plugin.overnet.gui.action.DownloadLinkAction;
56  import org.xnap.plugin.overnet.net.DownloadTable;
57  import org.xnap.plugin.overnet.net.OvernetCore;
58  import org.xnap.plugin.overnet.net.OvernetSearch;
59  import org.xnap.plugin.overnet.net.UploadTable;
60  import org.xnap.plugin.overnet.net.msg.MessageHandler;
61  import org.xnap.plugin.overnet.net.msg.client.StopMessage;
62  import org.xnap.plugin.overnet.util.OvernetPreferences;
63  import org.xnap.search.MediaType;
64  import org.xnap.search.Search;
65  import org.xnap.search.SearchFilter;
66  import org.xnap.search.SearchManager;
67  import org.xnap.search.SearchProvider;
68  import org.xnap.util.State;
69  import org.xnap.util.VersionParser;
70  
71  /***
72   * Provides a plugin that can control an overnet core.
73   *
74   * <p>OvernetPlugin follows the singleton pattern.
75   */
76  public class OvernetPlugin extends AbstractPlugin
77  	implements SearchProvider, StateListener
78  {
79      
80      //--- Constant(s) ---
81  
82  	public static final String ICON_FILENAME = "toolbar-overnet-logo.png";
83  				  
84  	public static final Icon ICON_16 = IconHelper.getIcon(ICON_FILENAME, 16, 
85  														  false);
86  				  
87  	public static final String NEW_COMMANDLINE_VERSION = "overnet0.51.2";
88  				  
89  	public static final String OLD_COMMANDLINE_OPTION = "-";
90  
91  	public static final String NEW_COMMANDLINE_OPTION = "-g";
92  
93  	/***
94  	 * The plugin's instance.
95  	 */
96  	private static OvernetPlugin instance = null;
97  	/***
98  	 * Handles Messagelisteners.
99  	 */
100 	private MessageHandler messageHandler;
101 	/***
102 	 * Used plugin-internally to map status messages to the corresponding
103 	 * uploads.
104 	 */
105 	private UploadTable uploadTable;
106 	/***
107 	 * Used plugin-internally to map status messages to the corresponding
108 	 * downloads.
109 	 */
110 	private DownloadTable downloadTable;
111 	/***
112 	 * Holds the connection to the core. Does all the sending and receiving of
113 	 * messages.
114 	 */
115 	private OvernetCore core;
116 	/***
117 	 * Holds the core process if the core was started from within the plugin.
118 	 */
119 	private Process coreProcess = null;
120 	/***
121 	 * Is notified when the preferences dialog becomes visible and adds the
122 	 * plugin's dialog.
123 	 */
124 	private PreferencesDialogListener listener;
125 	/***
126 	 * Menu plugged into XNap's plugins menu.
127 	 */
128 	private JMenu jmOvernet;
129 	/***
130 	 * The overnet gui panel.
131 	 */
132 	private OvernetPanel jpOvernet;
133 	/***
134 	 * The overnet status panel.
135 	 */
136 	private OvernetStatusPanel spOvernet;
137 	/***
138 	 * Overnet's manual helpset. Not used yet.
139 	 */
140 	private HelpSet hs;
141 	/***
142 	 * The shortcut for switching to the overnet panel.
143 	 */
144 	private ActionShortcut focusShortcut;
145 
146 	private static Logger logger = Logger.getLogger(OvernetPlugin.class);
147 
148     //--- Constructor(s) ---
149 
150     public OvernetPlugin()
151     {
152     }
153 
154     //--- Method(s) ---
155 
156 	public OvernetCore getCore()
157 	{
158 		return core;
159 	}
160 
161     public static OvernetPlugin getInstance()
162     {
163 		return instance;
164     }
165 	
166 	public String getName()
167 	{
168 		return getInfo().getName();
169 	}
170 
171     public void start()
172     {
173 		instance = this;
174 		messageHandler = new MessageHandler();
175 		downloadTable = new DownloadTable();
176 		uploadTable = new UploadTable();
177 
178 		OvernetPreferences.getInstance();
179 
180 		core = new OvernetCore(messageHandler);
181 		core.addStateListener(this);
182 
183 		startCoreConnection();
184     }
185 
186     public void startGUI()
187     {
188 		listener = new PreferencesDialogListener();
189 		XNapFacade.addPluginPreferencesDialogListener(listener);
190 
191 		initializeMenu();
192 		XNapFrame.getInstance().getMainMenuBar().addPluginMenu(jmOvernet);
193 
194 		jpOvernet = new OvernetPanel();
195 		jpOvernet.setName("overnet");
196 		XNapFrame.getInstance().insertTab
197 			(getName(), IconHelper.getListIcon(ICON_FILENAME), jpOvernet);
198 
199 		spOvernet = new OvernetStatusPanel();
200 		core.addStateListener(spOvernet);
201 		StatusBar.getInstance().addComponent(spOvernet);
202 
203 		// register shortcut
204 		FocusAction fa = new FocusAction();
205 		fa.putValue(Action.NAME, XNap.tr("Overnet"));
206 		fa.putValue(Action.SHORT_DESCRIPTION, 
207 						XNap.tr("Switches to Overnet panel"));
208 		fa.putValue(Action.ACTION_COMMAND_KEY, "overnet");
209 		fa.putValue(IconHelper.XNAP_ICON, ICON_FILENAME);
210 		fa.putValue(AbstractXNapAction.DEFAULT_KEYSTROKE,
211 						GUIHelper.getMenuKeyStroke(KeyEvent.VK_O));
212 		focusShortcut = ShortcutManager.getInstance().add
213 			(fa, (JComponent)XNapFrame.getInstance().getContentPane(), 
214 			 OvernetPreferences.getInstance());
215 
216 		/* show prefs panel in dialog when plugin is started for the first
217            time.  */
218 		if (!OvernetPreferences.getInstance().getBoolean("seenStartupWizard")) {
219 			DefaultPrefsWizardDialog dfd 
220 				= new DefaultPrefsWizardDialog(OvernetPreferences.getInstance(),
221 											   "seenStartupWizard");
222 			dfd.addPanel(new OvernetPreferencesPanel());
223 			dfd.pack();
224 			dfd.show(XNapFrame.getInstance());
225 		}
226 
227 		//  hs = HelpManager.loadHelpSet("overnet-manual");
228 //  		HelpManager.add(hs);
229     }
230 
231 	/***
232 	 * Starts a core process if the core command is specified in the
233 	 * preferences.
234 	 */
235 	private void startCoreConnection()
236 	{
237 		OvernetPreferences prefs = OvernetPreferences.getInstance();
238 		String command = prefs.getCoreCommand();
239 
240 		if (prefs.getStartCore() && command.length() > 0) {
241 			String[] args = new String[] 
242 				{ command, 
243 				  prefs.getUseCustomCommandLineOption() ? 
244 				  prefs.getCommandLineOption()
245 				  : getCommandLineOptions(command) };
246 			try {
247 				logger.debug("launching core " + command + " " + args[1]);
248 				coreProcess = Runtime.getRuntime().exec
249 					(args, null, (new File(command)).getParentFile());
250 			}
251 			catch (IOException ie) {
252 				logger.debug("Could not launch core", ie);
253 			}
254 		}
255 
256 		// connect to core after 5 seconds delay if it was just launched
257 		core.connect(prefs.getStartCore() ? 5 * 1000 : 0);
258 	}
259 
260 	public static String getCommandLineOptions(String command)
261 	{
262 		String core = new File(command).getName();
263 		return VersionParser.compare(core, NEW_COMMANDLINE_VERSION) < 0 ?
264 			OLD_COMMANDLINE_OPTION : NEW_COMMANDLINE_OPTION;
265 	}
266 
267 	/***
268 	 * Closes the core connection and kills the core process if it was started
269 	 * from within the plugin.
270 	 */
271 	private void stopCoreConnection()
272 	{
273 		if (coreProcess != null) {
274 			if (core.getState() == State.CONNECTED) {
275 				OvernetCore.send(new StopMessage());
276 			}
277 			core.stop();
278 			coreProcess.destroy();
279 			coreProcess = null;
280 		}
281 		else {
282 			core.stop();
283 		}
284 
285 		core.removeStateListener(this);
286 		core = null;
287 	}
288 
289     private void initializeMenu()
290     {
291 		jmOvernet = new XNapMenu(getName());
292 		jmOvernet.setIcon(IconHelper.getMenuIcon(ICON_FILENAME));
293 		jmOvernet.add(new XNapMenuItem(new ConnectAction()));
294 		jmOvernet.add(new XNapMenuItem(new DisconnectAction()));
295 		jmOvernet.addSeparator();
296 		jmOvernet.add(new XNapMenuItem(new DownloadLinkAction()));
297     }
298 
299 	public static MessageHandler getMessageHandler()
300 	{
301 		return instance.messageHandler;
302 	}
303 
304 	/***
305 	 * Implements the {@link StateListener} interface.
306 	 */
307 	public void stateChanged(StateEvent event)
308 	{
309 		if (core.getState() == State.DISCONNECTED) {
310 			disposeTransfers();
311 		}
312 	}
313 
314 	private void disposeTransfers()
315 	{
316 		downloadTable.dispose();
317 		uploadTable.dispose();
318 	}
319 
320     public void stop()
321     {
322 		stopCoreConnection();
323 
324 		disposeTransfers();
325 		// downloadTable = null;
326 // 		uploadTable = null;
327 // 		messageHandler = null;
328 		
329 		OvernetPreferences.disposeInstance();
330 		// we can not cleanly unload the plugin...
331 		//singleton = null;
332     }
333 
334     public void stopGUI()
335     {
336 		listener.dispose();
337 		XNapFacade.removePluginPreferencesDialogListener(listener);
338 		listener = null;
339 
340 		XNapFrame.getInstance().getMainMenuBar().removePluginMenu(jmOvernet);
341 		jmOvernet = null;
342 
343 		XNapFrame.getInstance().removeTab(jpOvernet);
344 		jpOvernet.dispose();
345 		jpOvernet = null;
346 		
347 		StatusBar.getInstance().removeComponent(spOvernet);
348 		core.removeStateListener(spOvernet);
349 		spOvernet = null;
350 
351 		ShortcutManager.getInstance().remove
352 			(focusShortcut, (JComponent)XNapFrame.getInstance().getContentPane());
353 
354 		//  HelpManager.remove(hs);
355 //  		hs = null;
356     }
357 
358 	public MediaType[] getSupportedMediaTypes()
359 	{
360 		return new MediaType[] { SearchManager.MEDIA_ANYTHING };
361     }
362 
363 	public Search search(SearchFilter filter)
364 	{
365 		return new OvernetSearch(filter);
366 	}
367 
368     // --- Inner Class(es) ---
369 
370 	private class PreferencesDialogListener
371 		extends AbstractPreferencesDialogListener
372 	{
373 		public void addPanels(AbstractPreferencesDialog dialog, List panels)
374 		{
375 			OvernetPreferencesPanel opp = new OvernetPreferencesPanel();
376 			panels.add(opp);
377 			panels.add(dialog.addPanel(opp, ICON_FILENAME));
378 		}
379 	}
380 }
381 
382