1 /*
2  * File    : PluginInterfaceImpl.java
3  * Created : 12 nov. 2003
4  * By      : Olivier
5  *
6  * Azureus - a Java Bittorrent client
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details ( see the LICENSE file ).
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 package org.gudy.azureus2.pluginsimpl.local;
24 
25 import java.util.*;
26 import java.io.File;
27 import java.net.URL;
28 
29 import org.gudy.azureus2.platform.PlatformManagerFactory;
30 import org.gudy.azureus2.plugins.*;
31 import org.gudy.azureus2.plugins.dht.mainline.*;
32 import org.gudy.azureus2.plugins.logging.Logger;
33 import org.gudy.azureus2.plugins.messaging.MessageManager;
34 import org.gudy.azureus2.plugins.network.ConnectionManager;
35 import org.gudy.azureus2.pluginsimpl.local.deprecate.PluginDeprecation;
36 import org.gudy.azureus2.pluginsimpl.local.dht.mainline.*;
37 import org.gudy.azureus2.pluginsimpl.local.clientid.ClientIDManagerImpl;
38 import org.gudy.azureus2.pluginsimpl.local.ddb.DDBaseImpl;
39 import org.gudy.azureus2.pluginsimpl.local.download.DownloadManagerImpl;
40 import org.gudy.azureus2.pluginsimpl.local.ipc.IPCInterfaceImpl;
41 import org.gudy.azureus2.pluginsimpl.local.ipfilter.IPFilterImpl;
42 import org.gudy.azureus2.pluginsimpl.local.logging.LoggerImpl;
43 import org.gudy.azureus2.pluginsimpl.local.messaging.MessageManagerImpl;
44 import org.gudy.azureus2.pluginsimpl.local.network.ConnectionManagerImpl;
45 import org.gudy.azureus2.pluginsimpl.local.sharing.ShareManagerImpl;
46 import org.gudy.azureus2.pluginsimpl.local.torrent.TorrentManagerImpl;
47 import org.gudy.azureus2.pluginsimpl.local.tracker.*;
48 import org.gudy.azureus2.pluginsimpl.local.ui.*;
49 import org.gudy.azureus2.pluginsimpl.local.ui.config.*;
50 import org.gudy.azureus2.pluginsimpl.local.utils.*;
51 import org.gudy.azureus2.pluginsimpl.local.update.*;
52 import org.gudy.azureus2.plugins.ipc.IPCInterface;
53 import org.gudy.azureus2.plugins.ipfilter.IPFilter;
54 import org.gudy.azureus2.plugins.tracker.Tracker;
55 import org.gudy.azureus2.plugins.ui.config.Parameter;
56 import org.gudy.azureus2.plugins.ui.config.PluginConfigUIFactory;
57 import org.gudy.azureus2.plugins.platform.PlatformManager;
58 import org.gudy.azureus2.plugins.sharing.*;
59 import org.gudy.azureus2.plugins.clientid.ClientIDManager;
60 import org.gudy.azureus2.plugins.ddb.DistributedDatabase;
61 import org.gudy.azureus2.plugins.download.*;
62 import org.gudy.azureus2.plugins.torrent.*;
63 import org.gudy.azureus2.plugins.ui.*;
64 import org.gudy.azureus2.plugins.ui.config.ConfigSection;
65 import org.gudy.azureus2.plugins.utils.*;
66 import org.gudy.azureus2.plugins.update.*;
67 
68 import org.gudy.azureus2.core3.util.*;
69 import org.gudy.azureus2.core3.logging.*;
70 
71 import com.aelitis.azureus.core.AzureusCoreComponent;
72 import com.aelitis.azureus.core.util.CopyOnWriteList;
73 
74 
75 
76 /**
77  * @author Olivier
78  *
79  */
80 public final class
81 PluginInterfaceImpl
82 	implements PluginInterface, AzureusCoreComponent
83 {
84 	private static final LogIDs LOGID = org.gudy.azureus2.core3.logging.LogIDs.PLUGIN;
85 
86   private Plugin				plugin;
87   private PluginInitializer		initialiser;
88   private Object				initialiser_key;
89   protected ClassLoader			class_loader;
90 
91   private CopyOnWriteList<PluginListener>		listeners 				= new CopyOnWriteList<PluginListener>();
92   private Set<PluginListener>					init_complete_fired_set	= new HashSet<PluginListener>();
93 
94   private CopyOnWriteList<PluginEventListener>		event_listeners	= new CopyOnWriteList<PluginEventListener>();
95   private String				key;
96   private String 				pluginConfigKey;
97   private Properties 			props;
98   private String 				pluginDir;
99   private PluginConfigImpl		config;
100   private String				plugin_version;
101   private Logger				logger;
102   private IPCInterfaceImpl		ipc_interface;
103   protected List				children		= new ArrayList();
104   private PluginStateImpl       state;
105 
106   /**
107    * This is the plugin ID value we were given when we were created.
108    *
109    * We might use it, but it depends what value is the plugins properties
110    * (which will override this value).
111    */
112   private String				given_plugin_id;
113 
114   /**
115    * We store this value as soon as someone calls getPluginID(), meaning
116    * we will return a consistent value for the plugin's lifetime.
117    */
118   private String                plugin_id_to_use;
119 
120   protected
PluginInterfaceImpl( Plugin _plugin, PluginInitializer _initialiser, Object _initialiser_key, ClassLoader _class_loader, List<File> _verified_files, String _key, Properties _props, String _pluginDir, String _plugin_id, String _plugin_version )121   PluginInterfaceImpl(
122   		Plugin				_plugin,
123   		PluginInitializer	_initialiser,
124 		Object				_initialiser_key,
125 		ClassLoader			_class_loader,
126 		List<File>			_verified_files,
127 		String 				_key,
128 		Properties 			_props,
129 		String 				_pluginDir,
130 		String				_plugin_id,
131 		String				_plugin_version )
132 
133   	throws PluginException
134   {
135 	  	// check we're being created by the core
136 
137 	  StackTraceElement[] stack = Thread.currentThread().getStackTrace();
138 
139 	  int	pos = 0;
140 
141 	  while( !stack[pos].getClassName().equals( PluginInterfaceImpl.class.getName())){
142 
143 		  pos++;
144 	  }
145 
146 	  String caller_class = stack[pos+1].getClassName();
147 
148 	  if ( !(	caller_class.equals( "org.gudy.azureus2.pluginsimpl.local.PluginInitializer" ) ||
149 			  	caller_class.equals( "org.gudy.azureus2.pluginsimpl.local.PluginInterfaceImpl" ))){
150 
151 		  throw( new PluginException( "Invalid caller" ));
152 	  }
153 
154 	  	// check we haven't been subclassed
155 
156 	  String class_name = getClass().getCanonicalName();
157 
158 	  if ( class_name == null || !class_name.equals( "org.gudy.azureus2.pluginsimpl.local.PluginInterfaceImpl" )){
159 
160 		  throw( new PluginException( "Subclassing not permitted" ));
161 	  }
162 
163 	  plugin				= _plugin;
164 	  initialiser			= _initialiser;
165 	  initialiser_key		= _initialiser_key;
166 	  class_loader			= _class_loader;
167 	  key					= _key;
168 	  pluginConfigKey 		= "Plugin." + _key;
169 	  props 				= new propertyWrapper(_props );
170 	  pluginDir 			= _pluginDir;
171 	  config 				= new PluginConfigImpl(this,pluginConfigKey);
172 	  given_plugin_id    	= _plugin_id;
173 	  plugin_version		= _plugin_version;
174 	  ipc_interface			= new IPCInterfaceImpl( initialiser, plugin );
175 	  state               	= new PluginStateImpl(this, initialiser);
176 
177 	  boolean verified 	= false;
178 	  boolean bad		= false;
179 
180 	  if ( _plugin_id.endsWith( "_v" )){
181 
182 		  if ( plugin.getClass() == FailedPlugin.class ){
183 
184 			  verified = true;
185 
186 		  }else{
187 		      if ( _verified_files != null  ){
188 
189 	    		  File jar = FileUtil.getJarFileFromClass( plugin.getClass());
190 
191 	    		  if ( jar != null ){
192 
193 	    			  for ( File file: _verified_files ){
194 
195 	    				  if ( file.equals( jar )){
196 
197 	    					  verified = true;
198 	    				  }
199 	    			  }
200 	    		  }
201 		      }
202 		  }
203 
204 	      if ( !verified ){
205 
206 	    	  bad = true;
207 	      }
208 	  }
209 
210 	  PluginInitializer.setVerified( this, plugin, verified, bad );
211   }
212 
213   	public Plugin
getPlugin()214 	getPlugin()
215 	{
216   		return( plugin );
217 	}
218 
isOperational()219     public boolean isOperational() {
220     	PluginDeprecation.call("isOperational", this.given_plugin_id);
221     	return getPluginState().isOperational();
222     }
223 
224   	public Object
getInitializerKey()225 	getInitializerKey()
226 	{
227   		return( initialiser_key );
228   	}
229 
230   	public PluginManager
getPluginManager()231 	getPluginManager()
232 	{
233   		return( initialiser.getPluginManager());
234   	}
235 
getApplicationName()236   	public String getApplicationName() {
237   		return Constants.APP_NAME;
238   	}
239 
240 	public String
getAzureusName()241 	getAzureusName()
242 	{
243 		return( Constants.AZUREUS_NAME );
244 	}
245 
246 	public String
getAzureusVersion()247 	getAzureusVersion()
248 	{
249 		return( Constants.AZUREUS_VERSION );
250 	}
251 
addConfigSection(ConfigSection section)252   public void addConfigSection(ConfigSection section)
253   {
254 	// Method is used by autocat.
255   	ConfigSectionRepository.getInstance().addConfigSection(section, this);
256   }
257 
removeConfigSection(ConfigSection section)258   public void removeConfigSection(ConfigSection section)
259   {
260   	ConfigSectionRepository.getInstance().removeConfigSection(section);
261   }
262 
getConfigSections()263   public ConfigSection[] getConfigSections() {
264   	ArrayList<ConfigSection> list = ConfigSectionRepository.getInstance().getList();
265   	for (Iterator<ConfigSection> iter = list.iterator(); iter.hasNext();) {
266 			ConfigSection configSection = iter.next();
267 			if (configSection instanceof ConfigSectionHolder) {
268 				if (((ConfigSectionHolder)configSection).getPluginInterface() != this) {
269 					iter.remove();
270 				}
271 			}
272 		}
273   	return list.toArray(new ConfigSection[0]);
274   }
275 
276   /**
277    * @deprecated
278    */
openTorrentFile(String fileName)279   public void openTorrentFile(String fileName) {
280 	  PluginDeprecation.call("openTorrentFile", this.getPluginID());
281 	  try{
282 		  getDownloadManager().addDownload( new File(fileName));
283 	  }catch( DownloadException e ){
284 		  throw( new RuntimeException(e));
285 	  }
286   }
287 
288   /**
289    * @deprecated
290    */
openTorrentURL(String url)291   public void openTorrentURL(String url) {
292 	  PluginDeprecation.call("openTorrentURL", this.getPluginID());
293 	  try{
294 		  getDownloadManager().addDownload( new URL( url ));
295 	  }catch( Throwable e ){
296 		  throw( new RuntimeException(e));
297 	  }
298   }
299 
300   public void
setPluginName( String name )301   setPluginName(
302   	String	name )
303   {
304   	props.put( "plugin.name", name );
305   }
306 
getPluginName()307   public String getPluginName()
308   {
309   	String	name = null;
310 
311   	if ( props != null ){
312 
313   		name = (String)props.get( "plugin.name");
314   	}
315 
316   	if ( name == null ){
317 
318   		try{
319 
320   			name = new File(pluginDir).getName();
321 
322   		}catch( Throwable e ){
323 
324   		}
325   	}
326 
327   	if ( name == null || name.length() == 0 ){
328 
329   		name = plugin.getClass().getName();
330   	}
331 
332   	return( name );
333   }
334 
335   public void
setPluginVersion( String version )336   setPluginVersion(
337   	String	version )
338   {
339 	props.put( "plugin.version", version );
340   }
341 
342   public String
getPluginVersion()343   getPluginVersion()
344   {
345 	String	version = (String)props.get("plugin.version");
346 
347   	if ( version == null ){
348 
349   		version = plugin_version;
350   	}
351 
352   	return( version );
353   }
354 
355   public String
getPluginID()356   getPluginID()
357   {
358 	  String id = (String)props.get("plugin.id");
359 
360 	  // hack alert - azupdater needs to change its plugin id due to general hackage
361 
362 	  if ( id != null && id.equals( "azupdater" )){
363 
364 		  plugin_id_to_use = id;
365 	  }
366 
367 	  if (plugin_id_to_use != null) {return plugin_id_to_use;}
368 
369 	// Calculate what plugin ID value to use - look at the properties file
370 	// first, and if that isn't correct, base it on the given plugin ID
371 	// value we were given.
372 
373   	if (id == null) {id = given_plugin_id;}
374   	if (id == null) {id = "<none>";}
375 
376   	plugin_id_to_use = id;
377   	return plugin_id_to_use;
378   }
379 
isMandatory()380   public boolean isMandatory() {
381 	  PluginDeprecation.call("isMandatory", this.given_plugin_id);
382 	  return getPluginState().isMandatory();
383   }
384 
isBuiltIn()385   public boolean isBuiltIn() {
386 	  PluginDeprecation.call("isBuiltIn", this.given_plugin_id);
387 	  return getPluginState().isBuiltIn();
388   }
389 
getPluginProperties()390   public Properties getPluginProperties()
391   {
392     return(props);
393   }
394 
getPluginDirectoryName()395   public String getPluginDirectoryName() {
396     return pluginDir;
397   }
398 
getPerUserPluginDirectoryName()399   public String getPerUserPluginDirectoryName(){
400 	String name;
401 	if ( pluginDir == null ){
402 		name = getPluginID();
403 	}else{
404 		name = new File( pluginDir).getName();
405 	}
406 
407 	String str = new File(new File(SystemProperties.getUserPath(),"plugins"),name).getAbsolutePath();
408 
409 	if ( pluginDir == null ){
410 
411 		return( str );
412 	}
413 
414 	try{
415 		if ( new File( pluginDir ).getCanonicalPath().equals( new File( str ).getCanonicalPath())){
416 
417 			return( pluginDir );
418 		}
419 	}catch( Throwable e ){
420 
421 	}
422 
423 	return( str );
424   }
425 
426   public void
setPluginDirectoryName( String name )427   setPluginDirectoryName(
428   	String		name )
429   {
430   	initialiser_key	= new File(name);
431 
432   	pluginDir	= name;
433   }
434 
addConfigUIParameters(Parameter[] parameters, String displayName)435   public void addConfigUIParameters(Parameter[] parameters, String displayName) {
436   	ParameterRepository.getInstance().addPlugin(parameters, displayName);
437   }
438 
439 
getPluginconfig()440   public PluginConfig getPluginconfig() {
441     return config;
442   }
443 
444 
getPluginConfigUIFactory()445   public PluginConfigUIFactory getPluginConfigUIFactory() {
446     return new PluginConfigUIFactoryImpl(config,pluginConfigKey);
447   }
448 
449   public String
getPluginConfigKey()450   getPluginConfigKey()
451   {
452   	return( pluginConfigKey );
453   }
454 
getTracker()455   public Tracker getTracker() {
456   	return( TrackerImpl.getSingleton());
457   }
458 
459   public ShareManager
getShareManager()460   getShareManager()
461 
462   	throws ShareException
463   {
464   	return( ShareManagerImpl.getSingleton());
465   }
466 
467   public DownloadManager
getDownloadManager()468   getDownloadManager()
469   {
470   	return( DownloadManagerImpl.getSingleton(initialiser.getAzureusCore()));
471   }
472 
getMainlineDHTManager()473   public MainlineDHTManager getMainlineDHTManager() {
474 	  return new MainlineDHTManagerImpl(initialiser.getAzureusCore());
475   }
476 
477   public TorrentManager
getTorrentManager()478   getTorrentManager()
479   {
480   	return( TorrentManagerImpl.getSingleton().specialise( this ));
481   }
482 
getLogger()483   public Logger getLogger()
484   {
485   	if ( logger == null ){
486 
487   		logger = new LoggerImpl( this );
488   	}
489 
490   	return( logger );
491   }
492 
493   public IPFilter
getIPFilter()494   getIPFilter()
495   {
496   	return( new IPFilterImpl());
497   }
498 
499   public Utilities
getUtilities()500   getUtilities()
501   {
502   	return( new UtilitiesImpl( initialiser.getAzureusCore(), this ));
503   }
504 
505   public ShortCuts
getShortCuts()506   getShortCuts()
507   {
508   	return( new ShortCutsImpl(this));
509   }
510 
511   public UIManager
getUIManager()512   getUIManager()
513   {
514   	return( new UIManagerImpl( this ));
515   }
516 
517   public UpdateManager
getUpdateManager()518   getUpdateManager()
519   {
520   	return( UpdateManagerImpl.getSingleton( initialiser.getAzureusCore()));
521   }
522 
523 
524   protected void
unloadSupport()525   unloadSupport()
526   {
527 	  ipc_interface.unload();
528 
529 	  UIManagerImpl.unload( this );
530   }
531 
isUnloadable()532   public boolean isUnloadable() {
533 	  PluginDeprecation.call("unloadable", this.given_plugin_id);
534 	  return getPluginState().isUnloadable();
535   }
536 
reload()537   public void reload() throws PluginException {
538 	  PluginDeprecation.call("reload", this.given_plugin_id);
539 	  getPluginState().reload();
540   }
541 
unload()542   public void unload() throws PluginException {
543 	  PluginDeprecation.call("unload", this.given_plugin_id);
544 	  getPluginState().unload();
545   }
546 
uninstall()547   public void uninstall() throws PluginException {
548 	  PluginDeprecation.call("uninstall", this.given_plugin_id);
549 	  getPluginState().uninstall();
550   }
551 
552 	public boolean
isInitialisationThread()553 	isInitialisationThread()
554 	{
555 		return( initialiser.isInitialisationThread());
556 	}
557 
558 	 public ClientIDManager
getClientIDManager()559 	 getClientIDManager()
560 	 {
561 	 	return( ClientIDManagerImpl.getSingleton());
562 	 }
563 
564 
getConnectionManager()565    public ConnectionManager getConnectionManager() {
566      return ConnectionManagerImpl.getSingleton( initialiser.getAzureusCore());
567    }
568 
getMessageManager()569    public MessageManager getMessageManager() {
570      return MessageManagerImpl.getSingleton( initialiser.getAzureusCore() );
571    }
572 
573 
574    public DistributedDatabase
getDistributedDatabase()575    getDistributedDatabase()
576    {
577    	return( DDBaseImpl.getSingleton(initialiser.getAzureusCore()));
578    }
579 
580    public PlatformManager
getPlatformManager()581    getPlatformManager()
582    {
583 	   return( PlatformManagerFactory.getPlatformManager());
584    }
585 
586   protected void
initialisationComplete()587   initialisationComplete()
588   {
589 	  Iterator<PluginListener> it = listeners.iterator();
590 
591 	  while( it.hasNext()){
592 
593 		  try{
594 			  fireInitComplete( it.next());
595 
596 		  }catch( Throwable e ){
597 
598 			  Debug.printStackTrace( e );
599 		  }
600 	  }
601 
602 	  for (int i=0;i<children.size();i++){
603 
604 		  ((PluginInterfaceImpl)children.get(i)).initialisationComplete();
605 	  }
606   }
607 
608   protected void
fireInitComplete( PluginListener listener )609   fireInitComplete(
610 	PluginListener		listener )
611   {
612 	  synchronized( init_complete_fired_set ){
613 
614 		  if ( init_complete_fired_set.contains( listener )){
615 
616 			  return;
617 		  }
618 
619 		  init_complete_fired_set.add( listener );
620 	  }
621 
622 	  try {
623 	  	listener.initializationComplete();
624 	  } catch (Exception e) {
625 	  	Debug.out(e);
626 	  }
627   }
628 
629   protected void
closedownInitiated()630   closedownInitiated()
631   {
632 	  Iterator it = listeners.iterator();
633 
634 	  while( it.hasNext()){
635 
636 		  try{
637 			  ((PluginListener)it.next()).closedownInitiated();
638 
639 		  }catch( Throwable e ){
640 
641 			  Debug.printStackTrace( e );
642 		  }
643 	  }
644 
645 	  for (int i=0;i<children.size();i++){
646 
647 		  ((PluginInterfaceImpl)children.get(i)).closedownInitiated();
648 	  }
649   }
650 
651   protected void
closedownComplete()652   closedownComplete()
653   {
654 	  Iterator it = listeners.iterator();
655 
656 	  while( it.hasNext()){
657 
658 		  try{
659 			  ((PluginListener)it.next()).closedownComplete();
660 
661 		  }catch( Throwable e ){
662 
663 			  Debug.printStackTrace( e );
664 		  }
665 	  }
666 
667 	  for (int i=0;i<children.size();i++){
668 
669 		  ((PluginInterfaceImpl)children.get(i)).closedownComplete();
670 	  }
671   }
672 
673   public ClassLoader
getPluginClassLoader()674   getPluginClassLoader()
675   {
676   	return( class_loader );
677   }
678 
679 	public PluginInterface
getLocalPluginInterface( Class plugin_class, String id )680 	getLocalPluginInterface(
681 		Class		plugin_class,
682 		String		id )
683 
684 		throws PluginException
685 	{
686 		try{
687 			Plugin	p = (Plugin)plugin_class.newInstance();
688 
689 			// Discard plugin.id from the properties, we want the
690 			// plugin ID we create to take priority - not a value
691 			// from the original plugin ID properties file.
692 			Properties local_props = new Properties(props);
693 			local_props.remove("plugin.id");
694 
695 
696 	 		if( id.endsWith( "_v" )){
697 
698 	  			throw( new Exception( "Verified plugins must be loaded from a jar" ));
699 	  		}
700 
701 			PluginInterfaceImpl pi =
702 				new PluginInterfaceImpl(
703 			  		p,
704 			  		initialiser,
705 					initialiser_key,
706 					class_loader,
707 					null,
708 					key + "." + id,
709 					local_props,
710 					pluginDir,
711 					getPluginID() + "." + id,
712 					plugin_version );
713 
714 			initialiser.fireCreated( pi );
715 
716 			p.initialize( pi );
717 
718 			children.add( pi );
719 
720 			return( pi );
721 
722 		}catch( Throwable e ){
723 
724 			if ( e instanceof PluginException ){
725 
726 				throw((PluginException)e);
727 			}
728 
729 			throw( new PluginException( "Local initialisation fails", e ));
730 		}
731 	}
732 
733 	 public IPCInterfaceImpl
getIPC()734 	 getIPC()
735 	 {
736 		 return( ipc_interface );
737 	 }
738 
isShared()739 	public boolean isShared() {
740 		PluginDeprecation.call("isShared", this.given_plugin_id);
741 		return getPluginState().isShared();
742 	}
743 
744 
745 	// Not exposed in the interface.
setAsFailed()746 	void setAsFailed() {
747 		getPluginState().setDisabled(true);
748 		state.failed = true;
749 	}
750 
751 	protected void
destroy()752 	destroy()
753 	{
754 		class_loader = null;
755 
756 			// unhook the reference to the plugin but leave with a valid reference in case
757 			// something tries to use it
758 
759 		plugin = new FailedPlugin( "Plugin '" + getPluginID() + "' has been unloaded!", null );
760 	}
761 
762   public void
addListener( PluginListener l )763   addListener(
764   	PluginListener	l )
765   {
766   	listeners.add(l);
767 
768   	if ( initialiser.isInitialisationComplete()){
769 
770   		fireInitComplete( l );
771   	}
772   }
773 
774   public void
removeListener( final PluginListener l )775   removeListener(
776   	final PluginListener	l )
777   {
778   	listeners.remove(l);
779 
780   		// we want to remove this ref, but there is a *small* chance that there's a parallel thread firing the complete so
781   		// decrease chance of hanging onto unwanted ref
782 
783   	new DelayedEvent(
784   		"PIL:clear", 10000,
785   		new AERunnable()
786   		{
787   			public void
788   			runSupport()
789   			{
790   				synchronized( init_complete_fired_set ){
791 
792   					init_complete_fired_set.remove(l);
793   				}
794   			}
795   		});
796   }
797 
798   public void
addEventListener( final PluginEventListener l )799   addEventListener(
800 	  final PluginEventListener	l )
801   {
802 	  initialiser.runPEVTask(
803 		 new AERunnable()
804 		 {
805 			 public void
806 			 runSupport()
807 			 {
808 				 List<PluginEvent> events = initialiser.getPEVHistory();
809 
810 				 for ( PluginEvent event: events ){
811 
812 					 try{
813 						 l.handleEvent( event );
814 
815 					 }catch( Throwable e ){
816 
817 						 Debug.out( e );
818 					 }
819 				 }
820 				 event_listeners.add(l);
821 			 }
822 		 });
823   }
824 
825   public void
removeEventListener( final PluginEventListener l )826   removeEventListener(
827 	  final PluginEventListener	l )
828   {
829 	  initialiser.runPEVTask(
830 		 new AERunnable()
831 		 {
832 			 public void
833 			 runSupport()
834 			 {
835 				  event_listeners.remove(l);
836 			 }
837 		 });
838   }
839 
840   public void
firePluginEvent( final PluginEvent event )841   firePluginEvent(
842 	  final PluginEvent		event )
843   {
844 	  initialiser.runPEVTask(
845 		 new AERunnable()
846 		 {
847 			 public void
848 			 runSupport()
849 			 {
850 				 firePluginEventSupport( event );
851 			 }
852 		 });
853   }
854 
855   protected void
firePluginEventSupport( PluginEvent event )856   firePluginEventSupport(
857 	  PluginEvent		event )
858   {
859 	  Iterator<PluginEventListener> it = event_listeners.iterator();
860 
861 	  while( it.hasNext()){
862 
863 		  try{
864 			  PluginEventListener listener = it.next();
865 
866 			  listener.handleEvent( event );
867 
868 		  }catch( Throwable e ){
869 
870 			  Debug.printStackTrace( e );
871 		  }
872 	  }
873 
874 	  for (int i=0;i<children.size();i++){
875 
876 		  ((PluginInterfaceImpl)children.get(i)).firePluginEvent(event);
877 	  }
878   }
879 
880 	protected void
generateEvidence( IndentWriter writer )881 	generateEvidence(
882 		IndentWriter		writer )
883 	{
884 		writer.println( getPluginName());
885 
886 		try{
887 			writer.indent();
888 
889 			writer.println( "id:" + getPluginID() + ",version:" + getPluginVersion());
890 
891 			String user_dir 	= FileUtil.getUserFile( "plugins" ).toString();
892 			String shared_dir 	= FileUtil.getApplicationFile( "plugins" ).toString();
893 
894 			String	plugin_dir = getPluginDirectoryName();
895 
896 			String	type;
897 			boolean	built_in = false;
898 
899 			if ( plugin_dir.startsWith( shared_dir )){
900 
901 				type = "shared";
902 
903 			}else	if ( plugin_dir.startsWith( user_dir )){
904 
905 				type = "per-user";
906 
907 			}else{
908 
909 				built_in = true;
910 
911 				type = "built-in";
912 			}
913 
914 			PluginState ps = getPluginState();
915 
916 			String	info = getPluginconfig().getPluginStringParameter( "plugin.info" );
917 
918 			writer.println( "type:" + type + ",enabled=" + !ps.isDisabled() + ",load_at_start=" + ps.isLoadedAtStartup() + ",operational=" + ps.isOperational() + (info==null||info.length()==0?"":( ",info=" + info )));
919 
920 			if ( ps.isOperational()){
921 
922 				Plugin plugin = getPlugin();
923 
924 				if ( plugin instanceof AEDiagnosticsEvidenceGenerator ){
925 
926 					try{
927 						writer.indent();
928 
929 						((AEDiagnosticsEvidenceGenerator)plugin).generate( writer );
930 
931 					}catch( Throwable e ){
932 
933 						writer.println( "Failed to generate plugin-specific info: " + Debug.getNestedExceptionMessage( e ));
934 
935 					}finally{
936 
937 						writer.exdent();
938 					}
939 				}
940 			}else{
941 				if ( !built_in ){
942 
943 					File dir = new File( plugin_dir );
944 
945 					if ( dir.exists()){
946 
947 						String[] files = dir.list();
948 
949 						if ( files != null ){
950 
951 							String	files_str = "";
952 
953 							for ( String f: files ){
954 
955 								files_str += (files_str.length()==0?"":", ") + f;
956 							}
957 
958 							writer.println( "    files: " + files_str );
959 						}
960 					}
961 				}
962 			}
963 		}finally{
964 
965 			writer.exdent();
966 		}
967 	}
968 
isDisabled()969 	public boolean isDisabled() {
970 		PluginDeprecation.call("isDisabled", this.given_plugin_id);
971 		return getPluginState().isDisabled();
972 	}
973 
setDisabled(boolean disabled)974 	public void setDisabled(boolean disabled) {
975 		PluginDeprecation.call("setDisabled", this.given_plugin_id);
976 		getPluginState().setDisabled(disabled);
977 	}
978 
getPluginState()979 	public PluginState getPluginState() {
980 		return this.state;
981 	}
982 
getPluginStateImpl()983 	PluginStateImpl getPluginStateImpl() {
984 		return this.state;
985 	}
986 
987 
988   	// unfortunately we need to protect ourselves against the plugin itself trying to set
989   	// plugin.version and plugin.id as this screws things up if they get it "wrong".
990   	// They should be setting these things in the plugin.properties file
991   	// currently the RSSImport plugin does this (version 1.1 sets version as 1.0)
992 
993   protected class
994   propertyWrapper
995   	extends Properties
996   {
997   	protected boolean	initialising	= true;
998 
999   	protected
propertyWrapper( Properties _props )1000 	propertyWrapper(
1001 		Properties	_props )
1002 	{
1003   		Iterator it = _props.keySet().iterator();
1004 
1005   		while( it.hasNext()){
1006 
1007   			Object	key = it.next();
1008 
1009   			put( key, _props.get(key));
1010   		}
1011 
1012   		initialising	= false;
1013   	}
1014 
1015   	public Object
setProperty( String str, String val )1016 	setProperty(
1017 		String		str,
1018 		String		val )
1019 	{
1020   			// if its us then we probably know what we're doing :P
1021 
1022   		if ( ! ( plugin.getClass().getName().startsWith( "org.gudy") || plugin.getClass().getName().startsWith( "com.aelitis."))){
1023 
1024 	  		if ( str.equalsIgnoreCase( "plugin.id" ) || str.equalsIgnoreCase("plugin.version" )){
1025 
1026 	  			if (org.gudy.azureus2.core3.logging.Logger.isEnabled())
1027 						org.gudy.azureus2.core3.logging.Logger
1028 								.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Plugin '"
1029 										+ getPluginName() + "' tried to set property '" + str
1030 										+ "' - action ignored"));
1031 
1032 	  			return( null );
1033 	  		}
1034   		}
1035 
1036   		return( super.setProperty( str, val ));
1037   	}
1038 
1039   	public Object
put( Object key, Object value )1040 	put(
1041 		Object	key,
1042 		Object	value )
1043 	{
1044 			// if its us then we probably know what we're doing :P
1045 
1046   		if ( ! ( plugin.getClass().getName().startsWith( "org.gudy") || plugin.getClass().getName().startsWith( "com.aelitis."))){
1047 
1048 	 		if ((!initialising ) && key instanceof String ){
1049 
1050 	  			String	k_str = (String)key;
1051 
1052 	  	 		if ( k_str.equalsIgnoreCase( "plugin.id" ) || k_str.equalsIgnoreCase("plugin.version" )){
1053 
1054 	  	 			if (org.gudy.azureus2.core3.logging.Logger.isEnabled())
1055 							org.gudy.azureus2.core3.logging.Logger.log(new LogEvent(LOGID,
1056 									LogEvent.LT_WARNING, "Plugin '" + getPluginName()
1057 											+ "' tried to set property '" + k_str
1058 											+ "' - action ignored"));
1059 
1060 	  	 			return( null );
1061 	  	 	  	}
1062 	  		}
1063   		}
1064 
1065   		return( super.put( key, value ));
1066   	}
1067 
1068   	public Object
get( Object key )1069 	get(
1070 		Object	key )
1071 	{
1072   		return( super.get(key));
1073   	}
1074   }
1075 }
1076