/* * File : PluginInitializer.java * Created : 2 nov. 2003 18:59:17 * By : Olivier * * Azureus - a Java Bittorrent client * * 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. * * 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 ( see the LICENSE file ). * * 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 */ package org.gudy.azureus2.pluginsimpl.local; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; import java.util.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.global.GlobalManager; import org.gudy.azureus2.core3.global.GlobalManagerListener; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.logging.*; import org.gudy.azureus2.core3.security.SESecurityManager; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.platform.PlatformManagerFactory; import org.gudy.azureus2.plugins.*; import org.gudy.azureus2.plugins.platform.PlatformManagerException; import org.gudy.azureus2.pluginsimpl.local.launch.PluginLauncherImpl; import org.gudy.azureus2.pluginsimpl.local.ui.UIManagerImpl; import org.gudy.azureus2.pluginsimpl.local.update.UpdateManagerImpl; import org.gudy.azureus2.pluginsimpl.local.utils.UtilitiesImpl; import org.gudy.azureus2.pluginsimpl.local.utils.UtilitiesImpl.runnableWithException; import org.gudy.azureus2.update.UpdaterUpdateChecker; import org.gudy.azureus2.update.UpdaterUtils; import com.aelitis.azureus.core.*; import com.aelitis.azureus.core.versioncheck.VersionCheckClient; /** * @author Olivier * */ public class PluginInitializer implements GlobalManagerListener, AEDiagnosticsEvidenceGenerator { public static final boolean DISABLE_PLUGIN_VERIFICATION = false; private static final LogIDs LOGID = LogIDs.CORE; public static final String INTERNAL_PLUGIN_ID = "<internal>"; // class name, plugin id, plugin key (key used for config props so if you change // it you'll need to migrate the config) // "id" is used when checking for updates // IF YOU ADD TO THE BUILTIN PLUGINS, AMEND PluginManagerDefault appropriately!!!! // Plugin ID constant // class // plugin id // plugin key for prefixing config data // report if not present // force re-enable if disabled by config private String[][] builtin_plugins = { { PluginManagerDefaults.PID_START_STOP_RULES, "com.aelitis.azureus.plugins.startstoprules.defaultplugin.StartStopRulesDefaultPlugin", "azbpstartstoprules", "", "true", "true"}, { PluginManagerDefaults.PID_REMOVE_RULES, "com.aelitis.azureus.plugins.removerules.DownloadRemoveRulesPlugin", "azbpremovalrules", "", "true", "false"}, { PluginManagerDefaults.PID_SHARE_HOSTER, "com.aelitis.azureus.plugins.sharing.hoster.ShareHosterPlugin", "azbpsharehoster", "ShareHoster", "true", "false"}, { PluginManagerDefaults.PID_PLUGIN_UPDATE_CHECKER, "org.gudy.azureus2.pluginsimpl.update.PluginUpdatePlugin", "azbppluginupdate", "PluginUpdate", "true", "true"}, { PluginManagerDefaults.PID_UPNP, "com.aelitis.azureus.plugins.upnp.UPnPPlugin", "azbpupnp", "UPnP", "true", "false"}, { PluginManagerDefaults.PID_DHT, "com.aelitis.azureus.plugins.dht.DHTPlugin", "azbpdht", "DHT", "true", "false"}, { PluginManagerDefaults.PID_DHT_TRACKER, "com.aelitis.azureus.plugins.tracker.dht.DHTTrackerPlugin", "azbpdhdtracker", "DHT Tracker", "true", "false"}, { PluginManagerDefaults.PID_MAGNET, "com.aelitis.azureus.plugins.magnet.MagnetPlugin", "azbpmagnet", "Magnet URI Handler", "true", "false"}, { PluginManagerDefaults.PID_CORE_UPDATE_CHECKER, "org.gudy.azureus2.update.CoreUpdateChecker", "azbpcoreupdater", "CoreUpdater", "true", "true"}, { PluginManagerDefaults.PID_CORE_PATCH_CHECKER, "org.gudy.azureus2.update.CorePatchChecker", "azbpcorepatcher", "CorePatcher", "true", "true"}, { PluginManagerDefaults.PID_PLATFORM_CHECKER, "org.gudy.azureus2.platform.PlatformManagerPluginDelegate", "azplatform2", "azplatform2", "true", "false"}, //{ PluginManagerDefaults.PID_JPC, // "com.aelitis.azureus.plugins.jpc.JPCPlugin", // "azjpc", // "azjpc", // "false" }, { PluginManagerDefaults.PID_EXTERNAL_SEED, "com.aelitis.azureus.plugins.extseed.ExternalSeedPlugin", "azextseed", "azextseed", "true", "false"}, { PluginManagerDefaults.PID_LOCAL_TRACKER, "com.aelitis.azureus.plugins.tracker.local.LocalTrackerPlugin", "azlocaltracker", "azlocaltracker", "true", "false"}, { PluginManagerDefaults.PID_NET_STATUS, "com.aelitis.azureus.plugins.net.netstatus.NetStatusPlugin", "aznetstat", "aznetstat", "true", "false"}, { PluginManagerDefaults.PID_BUDDY, "com.aelitis.azureus.plugins.net.buddy.BuddyPlugin", "azbuddy", "azbuddy", "true", "false"}, { PluginManagerDefaults.PID_RSS, "com.aelitis.azureus.core.rssgen.RSSGeneratorPlugin", "azintrss", "azintrss", "true", "false"}, /* disable until we can get some tracker admins to work on this { PluginManagerDefaults.PID_TRACKER_PEER_AUTH, "com.aelitis.azureus.plugins.tracker.peerauth.TrackerPeerAuthPlugin", "aztrackerpeerauth", "aztrackerpeerauth", "true", "false" }, */ }; static VerifiedPluginHolder verified_plugin_holder; static{ synchronized( PluginInitializer.class ){ verified_plugin_holder = new VerifiedPluginHolder(); } } // these can be removed one day private static String[][]default_version_details = { { "org.cneclipse.multiport.MultiPortPlugin", "multi-ports", "Mutli-Port Trackers", "1.0" }, }; private static PluginInitializer singleton; private static AEMonitor class_mon = new AEMonitor( "PluginInitializer"); private static List registration_queue = new ArrayList(); private static List initThreads = new ArrayList(1); private static AsyncDispatcher async_dispatcher = new AsyncDispatcher(); private static List<PluginEvent> plugin_event_history = new ArrayList<PluginEvent>(); private AzureusCoreOperation core_operation; private AzureusCore azureus_core; private PluginInterfaceImpl default_plugin; private PluginManager plugin_manager; private ClassLoader root_class_loader = getClass().getClassLoader(); private List loaded_pi_list = new ArrayList(); private static boolean loading_builtin; private List<Plugin> s_plugins = new ArrayList<Plugin>(); private List<PluginInterfaceImpl> s_plugin_interfaces = new ArrayList<PluginInterfaceImpl>(); private boolean initialisation_complete; private volatile boolean plugins_initialised; private Set<String> vc_disabled_plugins = VersionCheckClient.getSingleton().getDisabledPluginIDs(); public static PluginInitializer getSingleton( AzureusCore azureus_core, AzureusCoreOperation core_operation ) { try{ class_mon.enter(); if ( singleton == null ){ singleton = new PluginInitializer( azureus_core, core_operation ); } return( singleton ); }finally{ class_mon.exit(); } } private static PluginInitializer peekSingleton() { try{ class_mon.enter(); return( singleton ); }finally{ class_mon.exit(); } } protected static void queueRegistration( Class _class ) { try{ class_mon.enter(); if ( singleton == null ){ registration_queue.add( _class ); }else{ try{ singleton.initializePluginFromClass( _class, INTERNAL_PLUGIN_ID, _class.getName(), false, false, true); }catch(PluginException e ){ } } }finally{ class_mon.exit(); } } protected static void queueRegistration( Plugin plugin, String id ) { try{ class_mon.enter(); if ( singleton == null ){ registration_queue.add( new Object[]{ plugin, id }); }else{ try{ singleton.initializePluginFromInstance( plugin, id, plugin.getClass().getName()); }catch( Throwable e ){ Debug.out( e ); } } }finally{ class_mon.exit(); } } protected static boolean isLoadingBuiltin() { return( loading_builtin ); } public static void checkAzureusVersion( String name, Properties props, boolean alert_on_fail ) throws PluginException { String required_version = (String)props.get("plugin.azureus.min_version"); if (required_version == null) {return;} if (Constants.compareVersions(Constants.AZUREUS_VERSION, required_version) < 0) { String plugin_name_bit = name.length() > 0 ? (name+" "):""; String msg = "Plugin " + plugin_name_bit + "requires " + Constants.APP_NAME + " version " + required_version + " or higher"; if (alert_on_fail) { Logger.log(new LogAlert(LogAlert.REPEATABLE, LogAlert.AT_ERROR, msg)); } throw new PluginException(msg); } } public static void checkJDKVersion( String name, Properties props, boolean alert_on_fail ) throws PluginException { String required_jdk = (String)props.get( "plugin.jdk.min_version" ); if ( required_jdk != null ){ String actual_jdk = Constants.JAVA_VERSION; required_jdk = normaliseJDK( required_jdk ); actual_jdk = normaliseJDK( actual_jdk ); if ( required_jdk.length() == 0 || actual_jdk.length() == 0 ){ return; } if ( Constants.compareVersions( actual_jdk, required_jdk ) < 0 ){ String msg = "Plugin " + (name.length()>0?(name+" "):"" ) + "requires Java version " + required_jdk + " or higher"; if ( alert_on_fail ){ Logger.log(new LogAlert(LogAlert.REPEATABLE, LogAlert.AT_ERROR, msg)); } throw( new PluginException( msg )); } } } protected static String normaliseJDK( String jdk ) { try{ String str = ""; // take leading digit+. portion only for (int i=0;i<jdk.length();i++){ char c = jdk.charAt( i ); if ( c == '.' || Character.isDigit( c )){ str += c; }else{ break; } } // convert 5|6|... to 1.5|1.6 etc if ( Integer.parseInt( "" + str.charAt(0)) > 1 ){ str = "1." + str; } return( str ); }catch( Throwable e ){ return( "" ); } } protected PluginInitializer( AzureusCore _azureus_core, AzureusCoreOperation _core_operation ) { azureus_core = _azureus_core; AEDiagnostics.addEvidenceGenerator( this ); azureus_core.addLifecycleListener( new AzureusCoreLifecycleAdapter() { public void componentCreated( AzureusCore core, AzureusCoreComponent comp ) { if ( comp instanceof GlobalManager ){ GlobalManager gm = (GlobalManager)comp; gm.addListener( PluginInitializer.this ); } } }); core_operation = _core_operation; UpdateManagerImpl.getSingleton( azureus_core ); // initialise the update manager plugin_manager = PluginManagerImpl.getSingleton( this ); String dynamic_plugins = System.getProperty( "azureus.dynamic.plugins", null ); if ( dynamic_plugins != null ){ String[] classes = dynamic_plugins.split( ";" ); for ( String c: classes ){ try{ queueRegistration( Class.forName( c )); }catch( Throwable e ){ Debug.out( "Registration of dynamic plugin '" + c + "' failed", e ); } } } UpdaterUtils.checkBootstrapPlugins(); } protected void fireCreated( PluginInterfaceImpl pi ) { azureus_core.triggerLifeCycleComponentCreated( pi ); } protected void fireOperational( PluginInterfaceImpl pi, boolean op ) { fireEventSupport( op?PluginEvent.PEV_PLUGIN_OPERATIONAL:PluginEvent.PEV_PLUGIN_NOT_OPERATIONAL, pi ); } public static void addInitThread() { synchronized( initThreads ){ if ( initThreads.contains( Thread.currentThread())){ Debug.out( "Already added" ); } initThreads.add( Thread.currentThread()); } } public static void removeInitThread() { synchronized( initThreads ){ initThreads.remove( Thread.currentThread()); } } protected boolean isInitialisationThread() { synchronized( initThreads ){ return initThreads.contains(Thread.currentThread()); } } public List loadPlugins( AzureusCore core, boolean bSkipAlreadyLoaded, boolean load_external_plugins, boolean loading_for_startup, boolean initialise_plugins) { if ( bSkipAlreadyLoaded ){ // discard any failed ones List pis; synchronized( s_plugin_interfaces ){ pis = new ArrayList( s_plugin_interfaces ); } for (int i=0;i<pis.size();i++){ PluginInterfaceImpl pi = (PluginInterfaceImpl)pis.get(i); Plugin p = pi.getPlugin(); if ( p instanceof FailedPlugin ){ unloadPlugin( pi ); } } } List pluginLoaded = new ArrayList(); PluginManagerImpl.setStartDetails( core ); getRootClassLoader(); // first do explicit plugins File user_dir = FileUtil.getUserFile("plugins"); File app_dir = FileUtil.getApplicationFile("plugins"); int user_plugins = 0; int app_plugins = 0; if ( user_dir.exists() && user_dir.isDirectory()){ user_plugins = user_dir.listFiles().length; } if ( app_dir.exists() && app_dir.isDirectory()){ app_plugins = app_dir.listFiles().length; } // user ones first so they override app ones if present if (load_external_plugins) { pluginLoaded.addAll(loadPluginsFromDir(user_dir, 0, user_plugins + app_plugins, bSkipAlreadyLoaded, loading_for_startup, initialise_plugins)); if ( !user_dir.equals( app_dir )){ pluginLoaded.addAll(loadPluginsFromDir(app_dir, user_plugins, user_plugins + app_plugins, bSkipAlreadyLoaded, loading_for_startup, initialise_plugins)); } } else { if (Logger.isEnabled()) { Logger.log(new LogEvent(LOGID, "Loading of external plugins skipped")); } } if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Loading built-in plugins")); PluginManagerDefaults def = PluginManager.getDefaults(); for (int i=0;i<builtin_plugins.length;i++){ if ( def.isDefaultPluginEnabled( builtin_plugins[i][0])){ if (core_operation != null) { core_operation.reportCurrentTask(MessageText.getString("splash.plugin") + builtin_plugins[i][0]); } try{ loading_builtin = true; // lazyness here, for builtin we use static load method with default plugin interface // if we need to improve on this then we'll have to move to a system more akin to // the dir-loaded plugins Class cla = root_class_loader.loadClass( builtin_plugins[i][1]); Method load_method = cla.getMethod( "load", new Class[]{ PluginInterface.class }); load_method.invoke( null, new Object[]{ getDefaultInterfaceSupport() }); Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Built-in plugin '" + builtin_plugins[i][0] + "' ok")); }catch( NoSuchMethodException e ){ }catch( Throwable e ){ if ( builtin_plugins[i][4].equalsIgnoreCase("true" )){ Debug.printStackTrace( e ); Logger.log(new LogAlert(LogAlert.UNREPEATABLE, "Load of built in plugin '" + builtin_plugins[i][2] + "' fails", e)); } }finally{ loading_builtin = false; } }else{ if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Built-in plugin '" + builtin_plugins[i][2] + "' is disabled")); } } if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Loading dynamically registered plugins")); for (int i=0;i<registration_queue.size();i++){ Object entry = registration_queue.get(i); Class cla; String id; if ( entry instanceof Class ){ cla = (Class)entry; id = cla.getName(); }else{ Object[] x = (Object[])entry; Plugin plugin = (Plugin)x[0]; cla = plugin.getClass(); id = (String)x[1]; } try{ // lazyness here, for dynamic we use static load method with default plugin interface // if we need to improve on this then we'll have to move to a system more akin to // the dir-loaded plugins Method load_method = cla.getMethod( "load", new Class[]{ PluginInterface.class }); load_method.invoke( null, new Object[]{ getDefaultInterfaceSupport() }); }catch( NoSuchMethodException e ){ }catch( Throwable e ){ Debug.printStackTrace( e ); Logger.log(new LogAlert(LogAlert.UNREPEATABLE, "Load of dynamic plugin '" + id + "' fails", e)); } } return pluginLoaded; } private void getRootClassLoader() { // first do explicit plugins File user_dir = FileUtil.getUserFile("shared"); getRootClassLoader( user_dir ); File app_dir = FileUtil.getApplicationFile("shared"); if ( !user_dir.equals( app_dir )){ getRootClassLoader( app_dir ); } } private void getRootClassLoader( File dir ) { dir = new File( dir, "lib" ); if ( dir.exists() && dir.isDirectory()){ File[] files = dir.listFiles(); if ( files != null ){ files = PluginLauncherImpl.getHighestJarVersions( files, new String[]{ null }, new String[]{ null }, false ); for (int i=0;i<files.length;i++){ if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Share class loader extended by " + files[i].toString())); root_class_loader = PluginLauncherImpl.addFileToClassPath( PluginInitializer.class.getClassLoader(), root_class_loader, files[i] ); } } } } private List loadPluginsFromDir( File pluginDirectory, int plugin_offset, int plugin_total, boolean bSkipAlreadyLoaded, boolean loading_for_startup, boolean initialise) { List dirLoadedPIs = new ArrayList(); if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Plugin Directory is " + pluginDirectory)); if ( !pluginDirectory.exists() ){ FileUtil.mkdirs(pluginDirectory); } if( pluginDirectory.isDirectory()){ File[] pluginsDirectory = pluginDirectory.listFiles(); for(int i = 0 ; i < pluginsDirectory.length ; i++) { if( pluginsDirectory[i].getName().equals( "CVS" ) ) { if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Skipping plugin " + pluginsDirectory[i].getName())); continue; } if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Loading plugin " + pluginsDirectory[i].getName())); if(core_operation != null) { core_operation.reportCurrentTask(MessageText.getString("splash.plugin") + pluginsDirectory[i].getName()); } try{ List loaded_pis = loadPluginFromDir(pluginsDirectory[i], bSkipAlreadyLoaded, loading_for_startup, initialise); // save details for later initialisation loaded_pi_list.add( loaded_pis ); dirLoadedPIs.addAll( loaded_pis ); }catch( PluginException e ){ // already handled } if( core_operation != null ){ core_operation.reportPercent( (100 * (i + plugin_offset)) / plugin_total ); } } } return dirLoadedPIs; } private List loadPluginFromDir( File directory, boolean bSkipAlreadyLoaded, boolean loading_for_startup, boolean initialise) // initialise setting is used if loading_for_startup isnt throws PluginException { List loaded_pis = new ArrayList(); ClassLoader plugin_class_loader = root_class_loader; if( !directory.isDirectory()){ return( loaded_pis ); } String pluginName = directory.getName(); File[] pluginContents = directory.listFiles(); if ( pluginContents == null || pluginContents.length == 0){ return( loaded_pis ); } // first sanity check - dir must include either a plugin.properties or // at least one .jar file boolean looks_like_plugin = false; for (int i=0;i<pluginContents.length;i++){ String name = pluginContents[i].getName().toLowerCase(); if ( name.endsWith( ".jar") || name.equals( "plugin.properties" )){ looks_like_plugin = true; break; } } if ( !looks_like_plugin ){ if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Plugin directory '" + directory + "' has no plugin.properties " + "or .jar files, skipping")); return( loaded_pis ); } // take only the highest version numbers of jars that look versioned String[] plugin_version = {null}; String[] plugin_id = {null}; pluginContents = PluginLauncherImpl.getHighestJarVersions( pluginContents, plugin_version, plugin_id, true ); for( int i = 0 ; i < pluginContents.length ; i++){ File jar_file = pluginContents[i]; // migration hack for i18nAZ_1.0.jar if ( pluginContents.length > 1 ){ String name = jar_file.getName(); if ( name.startsWith( "i18nPlugin_" )){ // non-versioned version still there, rename it if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "renaming '" + name + "' to conform with versioning system")); jar_file.renameTo( new File( jar_file.getParent(), "i18nAZ_0.1.jar " )); continue; } } plugin_class_loader = PluginLauncherImpl.addFileToClassPath( root_class_loader, plugin_class_loader, jar_file); } String plugin_class_string = null; try { Properties props = new Properties(); File properties_file = new File(directory.toString() + File.separator + "plugin.properties"); try { // if properties file exists on its own then override any properties file // potentially held within a jar if ( properties_file.exists()){ FileInputStream fis = null; try{ fis = new FileInputStream( properties_file ); props.load( fis ); }finally{ if ( fis != null ){ fis.close(); } } }else{ if ( plugin_class_loader instanceof URLClassLoader ){ URLClassLoader current = (URLClassLoader)plugin_class_loader; URL url = current.findResource("plugin.properties"); if ( url != null ){ URLConnection connection = url.openConnection(); InputStream is = connection.getInputStream(); props.load(is); }else{ throw( new Exception( "failed to load plugin.properties from jars")); } }else{ throw( new Exception( "failed to load plugin.properties from dir or jars")); } } }catch( Throwable e ){ Debug.printStackTrace( e ); String msg = "Can't read 'plugin.properties' for plugin '" + pluginName + "': file may be missing"; Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, msg)); System.out.println( msg ); throw( new PluginException( msg, e )); } checkJDKVersion( pluginName, props, true ); checkAzureusVersion(pluginName, props, true); plugin_class_string = (String)props.get( "plugin.class"); if ( plugin_class_string == null ){ plugin_class_string = (String)props.get( "plugin.classes"); if ( plugin_class_string == null ){ // set so we don't bork later will npe plugin_class_string = ""; } } String plugin_name_string = (String)props.get( "plugin.name"); if ( plugin_name_string == null ){ plugin_name_string = (String)props.get( "plugin.names"); } int pos1 = 0; int pos2 = 0; while(true){ int p1 = plugin_class_string.indexOf( ";", pos1 ); String plugin_class; if ( p1 == -1 ){ plugin_class = plugin_class_string.substring(pos1).trim(); }else{ plugin_class = plugin_class_string.substring(pos1,p1).trim(); pos1 = p1+1; } PluginInterfaceImpl existing_pi = getPluginFromClass( plugin_class ); if ( existing_pi != null ){ if (bSkipAlreadyLoaded) { break; } // allow user dir entries to override app dir entries without warning File this_parent = directory.getParentFile(); File existing_parent = null; if ( existing_pi.getInitializerKey() instanceof File ){ existing_parent = ((File)existing_pi.getInitializerKey()).getParentFile(); } if ( this_parent.equals( FileUtil.getApplicationFile("plugins")) && existing_parent != null && existing_parent.equals( FileUtil.getUserFile( "plugins" ))){ // skip this overridden plugin if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Plugin '" + plugin_name_string + "/" + plugin_class + ": shared version overridden by user-specific one")); return( new ArrayList()); }else{ Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_WARNING, "Error loading '" + plugin_name_string + "', plugin class '" + plugin_class + "' is already loaded")); } }else{ String plugin_name = null; if ( plugin_name_string != null ){ int p2 = plugin_name_string.indexOf( ";", pos2 ); if ( p2 == -1 ){ plugin_name = plugin_name_string.substring(pos2).trim(); }else{ plugin_name = plugin_name_string.substring(pos2,p2).trim(); pos2 = p2+1; } } Properties new_props = (Properties)props.clone(); for (int j=0;j<default_version_details.length;j++){ if ( plugin_class.equals( default_version_details[j][0] )){ if ( new_props.get( "plugin.id") == null ){ new_props.put( "plugin.id", default_version_details[j][1]); } if ( plugin_name == null ){ plugin_name = default_version_details[j][2]; } if ( new_props.get( "plugin.version") == null ){ // no explicit version. If we've derived one then use that, otherwise defaults if ( plugin_version[0] != null ){ new_props.put( "plugin.version", plugin_version[0]); }else{ new_props.put( "plugin.version", default_version_details[j][3]); } } } } new_props.put( "plugin.class", plugin_class ); if ( plugin_name != null ){ new_props.put( "plugin.name", plugin_name ); } // System.out.println( "loading plugin '" + plugin_class + "' using cl " + classLoader); // if the plugin load fails we still need to generate a plugin entry // as this drives the upgrade process Throwable load_failure = null; String pid = plugin_id[0]==null?directory.getName():plugin_id[0]; List<File> verified_files = null; Plugin plugin = null; if ( vc_disabled_plugins.contains ( pid )){ log( "Plugin '" + pid + "' has been administratively disabled" ); }else{ try{ String cl_key = "plugin.cl.ext." + pid; String str = COConfigurationManager.getStringParameter( cl_key, null ); if ( str != null && str.length() > 0 ){ COConfigurationManager.removeParameter( cl_key ); plugin_class_loader = PluginLauncherImpl.extendClassLoader( root_class_loader, plugin_class_loader, new URL( str )); } }catch( Throwable e ){ } if ( pid.endsWith( "_v" )){ verified_files = new ArrayList<File>(); // re-verify jar files log( "Re-verifying " + pid ); for( int i = 0 ; i < pluginContents.length ; i++){ File jar_file = pluginContents[i]; if ( jar_file.getName().endsWith( ".jar" )){ try{ log( " verifying " + jar_file ); AEVerifier.verifyData( jar_file ); verified_files.add( jar_file ); log( " OK" ); }catch( Throwable e ){ String msg = "Error loading plugin '" + pluginName + "' / '" + plugin_class_string + "'"; Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, e)); plugin = new FailedPlugin(plugin_name,directory.getAbsolutePath()); } } } } if ( plugin == null ){ plugin = PluginLauncherImpl.getPreloadedPlugin( plugin_class ); if ( plugin == null ){ try{ try{ Class<Plugin> c = (Class<Plugin>)PlatformManagerFactory.getPlatformManager().loadClass( plugin_class_loader, plugin_class ); //Class c = plugin_class_loader.loadClass(plugin_class); plugin = c.newInstance(); try{ // kick off any pre-inits if ( plugin_class_loader instanceof URLClassLoader ){ URL[] urls = ((URLClassLoader)plugin_class_loader).getURLs(); for ( URL u: urls ){ String path = u.getPath(); if ( path.endsWith( ".jar" )){ int s1 = path.lastIndexOf( '/' ); int s2 = path.lastIndexOf( '\\' ); path = path.substring( Math.max( s1, s2 )+1); s2 = path.indexOf( '_' ); if ( s2 > 0 ){ path = path.substring( 0, s2 ); path = path.replaceAll( "-", "" ); String cl = "plugin.preinit." + pid + ".PI" + path; try{ Class pic = plugin_class_loader.loadClass( cl ); if ( pic != null ){ pic.newInstance(); } }catch( Throwable e ){ } } } } } }catch( Throwable e ){ } }catch( PlatformManagerException e ){ throw( e.getCause()); } }catch (java.lang.UnsupportedClassVersionError e) { plugin = new FailedPlugin(plugin_name,directory.getAbsolutePath()); // shorten stack trace load_failure = new UnsupportedClassVersionError(e.getMessage()); }catch( Throwable e ){ if ( e instanceof ClassNotFoundException && props.getProperty( "plugin.install_if_missing", "no" ).equalsIgnoreCase( "yes" )){ // don't report the failure }else{ load_failure = e; } plugin = new FailedPlugin(plugin_name,directory.getAbsolutePath()); } }else{ plugin_class_loader = plugin.getClass().getClassLoader(); } } MessageText.integratePluginMessages((String)props.get("plugin.langfile"),plugin_class_loader); PluginInterfaceImpl plugin_interface = new PluginInterfaceImpl( plugin, this, directory, plugin_class_loader, verified_files, directory.getName(), // key for config values new_props, directory.getAbsolutePath(), pid, plugin_version[0] ); boolean bEnabled = (loading_for_startup) ? plugin_interface.getPluginState().isLoadedAtStartup() : initialise; plugin_interface.getPluginState().setDisabled(!bEnabled); try{ Method load_method = plugin.getClass().getMethod( "load", new Class[]{ PluginInterface.class }); load_method.invoke( plugin, new Object[]{ plugin_interface }); }catch( NoSuchMethodException e ){ }catch( Throwable e ){ load_failure = e; } loaded_pis.add( plugin_interface ); if ( load_failure != null ){ plugin_interface.setAsFailed(); // don't complain about our internal one if ( !pid.equals(UpdaterUpdateChecker.getPluginID())){ String msg = MessageText.getString("plugin.init.load.failed", new String[]{ plugin_name==null?pluginName:plugin_name, directory.getAbsolutePath() }); LogAlert la; if ( load_failure instanceof UnsupportedClassVersionError ){ la = new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, msg + ".\n\n" + MessageText.getString("plugin.install.class_version_error")); }else if ( load_failure instanceof ClassNotFoundException ){ la = new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, msg + ".\n\n" + MessageText.getString("plugin.init.load.failed.classmissing") + "\n\n", load_failure ); }else{ la = new LogAlert(LogAlert.UNREPEATABLE, msg, load_failure); } Logger.log( la ); System.out.println( msg + ": " + load_failure); } } } } if ( p1 == -1 ){ break; } } return( loaded_pis ); }catch(Throwable e) { if ( e instanceof PluginException ){ throw((PluginException)e); } Debug.printStackTrace( e ); String msg = "Error loading plugin '" + pluginName + "' / '" + plugin_class_string + "'"; Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, e)); System.out.println( msg + ": " + e); throw( new PluginException( msg, e )); } } private void log( String str ) { if (Logger.isEnabled()){ Logger.log(new LogEvent(LOGID, str )); } } public void initialisePlugins() { try{ addInitThread(); final LinkedList initQueue = new LinkedList(); for (int i = 0; i < loaded_pi_list.size(); i++) { final int idx = i; initQueue.add(new Runnable() { public void run() { try { List l = (List) loaded_pi_list.get(idx); if (l.size() > 0) { PluginInterfaceImpl plugin_interface = (PluginInterfaceImpl) l.get(0); if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Initializing plugin '" + plugin_interface.getPluginName() + "'")); if (core_operation != null) { core_operation.reportCurrentTask(MessageText .getString("splash.plugin.init") + " " + plugin_interface.getPluginName()); } initialisePlugin(l); if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Initialization of plugin '" + plugin_interface.getPluginName() + "' complete")); } } catch ( Throwable e ){ // already handled } finally { if (core_operation != null) { core_operation.reportPercent((100 * (idx + 1)) / loaded_pi_list.size()); } } // some plugins try and steal the logger stdout redirects. // re-establish them if needed Logger.doRedirects(); } }); } // now do built in ones initQueue.add(new Runnable() { public void run() { if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Initializing built-in plugins")); } }); final PluginManagerDefaults def = PluginManager.getDefaults(); for (int i = 0; i < builtin_plugins.length; i++) { final int idx = i; initQueue.add(new Runnable() { public void run() { if (def.isDefaultPluginEnabled(builtin_plugins[idx][0])) { String id = builtin_plugins[idx][2]; String key = builtin_plugins[idx][3]; try { Class cla = root_class_loader.loadClass( builtin_plugins[idx][1]); if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Initializing built-in plugin '" + builtin_plugins[idx][2] + "'" )); initializePluginFromClass(cla, id, key, "true".equals(builtin_plugins[idx][5]), true, true); if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Initialization of built in plugin '" + builtin_plugins[idx][2] + "' complete")); } catch (Throwable e) { try { // replace it with a "broken" plugin instance initializePluginFromClass(FailedPlugin.class, id, key, false, false, true); } catch (Throwable f) { } if (builtin_plugins[idx][4].equalsIgnoreCase("true")) { Debug.printStackTrace(e); Logger.log(new LogAlert(LogAlert.UNREPEATABLE, "Initialization of built in plugin '" + builtin_plugins[idx][2] + "' fails", e)); } } } else { if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Built-in plugin '" + builtin_plugins[idx][2] + "' is disabled")); } } }); } initQueue.add(new Runnable() { public void run() { if (Logger.isEnabled()) Logger.log(new LogEvent(LOGID, "Initializing dynamically registered plugins")); } }); for (int i = 0; i < registration_queue.size(); i++) { final int idx = i; initQueue.add(new Runnable() { public void run() { try { Object entry = registration_queue.get(idx); if (entry instanceof Class) { Class cla = (Class) entry; singleton.initializePluginFromClass(cla, INTERNAL_PLUGIN_ID, cla .getName(), false, true, true); } else { Object[] x = (Object[]) entry; Plugin plugin = (Plugin) x[0]; singleton.initializePluginFromInstance(plugin, (String) x[1], plugin .getClass().getName()); } } catch (PluginException e) { } } }); } AEThread2 secondaryInitializer = new AEThread2("2nd PluginInitializer Thread",true) { public void run() { try{ addInitThread(); while( true ){ Runnable toRun; synchronized (initQueue){ if (initQueue.isEmpty()){ break; } toRun = (Runnable)initQueue.remove(0); } try{ toRun.run(); }catch( Throwable e ){ Debug.out(e); } } }finally{ removeInitThread(); } } }; secondaryInitializer.start(); while(true){ Runnable toRun; synchronized( initQueue ){ if( initQueue.isEmpty()){ break; } toRun = (Runnable)initQueue.remove(0); } try{ toRun.run(); }catch( Throwable e ){ Debug.out(e); } } secondaryInitializer.join(); registration_queue.clear(); plugins_initialised = true; fireEvent( PluginEvent.PEV_ALL_PLUGINS_INITIALISED ); }finally{ removeInitThread(); } } protected void checkPluginsInitialised() { if ( !plugins_initialised ){ Debug.out( "Wait until plugin initialisation is complete until doing this!" ); } } protected boolean isInitialized() { return( plugins_initialised ); } private void initialisePlugin( List l ) throws PluginException { PluginException last_load_failure = null; for (int i=0;i<l.size();i++){ final PluginInterfaceImpl plugin_interface = (PluginInterfaceImpl)l.get(i); if (plugin_interface.getPluginState().isDisabled()) { synchronized( s_plugin_interfaces ){ s_plugin_interfaces.add( plugin_interface ); } continue; } if ( plugin_interface.getPluginState().isOperational()){ continue; } Throwable load_failure = null; final Plugin plugin = plugin_interface.getPlugin(); try{ UtilitiesImpl.callWithPluginThreadContext( plugin_interface, new runnableWithException<PluginException>() { public void run() throws PluginException { fireCreated( plugin_interface ); plugin.initialize(plugin_interface); if (!(plugin instanceof FailedPlugin)){ plugin_interface.getPluginStateImpl().setOperational( true, false ); } } }); }catch( Throwable e ){ load_failure = e; } synchronized( s_plugin_interfaces ){ s_plugins.add( plugin ); s_plugin_interfaces.add( plugin_interface ); } if ( load_failure != null ){ Debug.printStackTrace( load_failure ); String msg = "Error initializing plugin '" + plugin_interface.getPluginName() + "'"; Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, load_failure)); System.out.println( msg + " : " + load_failure); last_load_failure = new PluginException( msg, load_failure ); } } if ( last_load_failure != null ){ throw( last_load_failure ); } } protected void initializePluginFromClass( final Class plugin_class, final String plugin_id, String plugin_config_key, boolean force_enabled, boolean loading_for_startup, boolean initialise) throws PluginException { if ( plugin_class != FailedPlugin.class && getPluginFromClass( plugin_class ) != null ){ Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_WARNING, "Error loading '" + plugin_id + "', plugin class '" + plugin_class.getName() + "' is already loaded")); return; } try{ final Plugin plugin = (Plugin) plugin_class.newInstance(); String plugin_name; if ( plugin_config_key.length() == 0 ){ plugin_name = plugin_class.getName(); int pos = plugin_name.lastIndexOf("."); if ( pos != -1 ){ plugin_name = plugin_name.substring( pos+1 ); } }else{ plugin_name = plugin_config_key; } Properties properties = new Properties(); // default plugin name properties.put( "plugin.name", plugin_name ); final PluginInterfaceImpl plugin_interface = new PluginInterfaceImpl( plugin, this, plugin_class, plugin_class.getClassLoader(), null, plugin_config_key, properties, "", plugin_id, null ); boolean bEnabled = (loading_for_startup) ? plugin_interface.getPluginState().isLoadedAtStartup() : initialise; /** * For some plugins, override any config setting which disables the plugin. */ if (force_enabled && !bEnabled) { plugin_interface.getPluginState().setLoadedAtStartup(true); bEnabled = true; Logger.log(new LogAlert(false, LogAlert.AT_WARNING, MessageText.getString( "plugins.init.force_enabled", new String[] {plugin_id} ))); } plugin_interface.getPluginState().setDisabled(!bEnabled); final boolean f_enabled = bEnabled; UtilitiesImpl.callWithPluginThreadContext( plugin_interface, new runnableWithException<PluginException>() { public void run() throws PluginException { try{ Method load_method = plugin_class.getMethod( "load", new Class[]{ PluginInterface.class }); load_method.invoke( plugin, new Object[]{ plugin_interface }); }catch( NoSuchMethodException e ){ }catch( Throwable e ){ Debug.printStackTrace( e ); Logger.log(new LogAlert(LogAlert.UNREPEATABLE, "Load of built in plugin '" + plugin_id + "' fails", e)); } if (f_enabled) { if ( core_operation != null ){ core_operation.reportCurrentTask(MessageText.getString("splash.plugin.init") + " " + plugin_interface.getPluginName()); } fireCreated( plugin_interface ); plugin.initialize(plugin_interface); if (!(plugin instanceof FailedPlugin)){ plugin_interface.getPluginStateImpl().setOperational( true, false ); } } } }); synchronized( s_plugin_interfaces ){ s_plugins.add( plugin ); s_plugin_interfaces.add( plugin_interface ); } }catch(Throwable e){ Debug.printStackTrace( e ); String msg = "Error loading internal plugin '" + plugin_class.getName() + "'"; Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, e)); System.out.println(msg + " : " + e); throw( new PluginException( msg, e )); } } protected void initializePluginFromInstance( final Plugin plugin, String plugin_id, String plugin_config_key ) throws PluginException { try{ final PluginInterfaceImpl plugin_interface = new PluginInterfaceImpl( plugin, this, plugin.getClass(), plugin.getClass().getClassLoader(), null, plugin_config_key, new Properties(), "", plugin_id, null ); UtilitiesImpl.callWithPluginThreadContext( plugin_interface, new UtilitiesImpl.runnableWithException<PluginException>() { public void run() throws PluginException { fireCreated( plugin_interface ); plugin.initialize(plugin_interface); if (!(plugin instanceof FailedPlugin)){ plugin_interface.getPluginStateImpl().setOperational( true, false ); } } }); synchronized( s_plugin_interfaces ){ s_plugins.add( plugin ); s_plugin_interfaces.add( plugin_interface ); } }catch(Throwable e){ Debug.printStackTrace( e ); String msg = "Error loading internal plugin '" + plugin.getClass().getName() + "'"; Logger.log(new LogAlert(LogAlert.UNREPEATABLE, msg, e)); System.out.println(msg + " : " + e); throw( new PluginException( msg, e )); } } protected void unloadPlugin( PluginInterfaceImpl pi ) { synchronized( s_plugin_interfaces ){ s_plugins.remove( pi.getPlugin()); s_plugin_interfaces.remove( pi ); } pi.unloadSupport(); for (int i=0;i<loaded_pi_list.size();i++){ List l = (List)loaded_pi_list.get(i); if ( l.remove(pi)){ if ( l.size() == 0 ){ loaded_pi_list.remove(i); } break; } } verified_plugin_holder.removeValue( pi ); } protected void reloadPlugin(PluginInterfaceImpl pi) throws PluginException { reloadPlugin(pi, false, true); } protected void reloadPlugin( PluginInterfaceImpl pi, boolean loading_for_startup, boolean initialise) throws PluginException { unloadPlugin( pi ); Object key = pi.getInitializerKey(); String config_key = pi.getPluginConfigKey(); if ( key instanceof File ){ List pis = loadPluginFromDir( (File)key, false, loading_for_startup, initialise); initialisePlugin( pis ); }else{ initializePluginFromClass( (Class) key, pi.getPluginID(), config_key, false, loading_for_startup, initialise ); } } protected AzureusCore getAzureusCore() { return( azureus_core ); } protected GlobalManager getGlobalManager() { return( azureus_core.getGlobalManager() ); } public static PluginInterface getDefaultInterface() { if (singleton == null) { throw new AzureusCoreException( "PluginInitializer not instantiated by AzureusCore.create yet"); } return( singleton.getDefaultInterfaceSupport()); } protected PluginInterface getDefaultInterfaceSupport() { synchronized( s_plugin_interfaces ){ if ( default_plugin == null ){ try{ default_plugin = new PluginInterfaceImpl( new Plugin() { public void initialize( PluginInterface pi) { } }, this, getClass(), getClass().getClassLoader(), null, "default", new Properties(), null, INTERNAL_PLUGIN_ID, null ); }catch( Throwable e ){ Debug.out( e ); } } } return( default_plugin ); } public void downloadManagerAdded( DownloadManager dm ) { } public void downloadManagerRemoved( DownloadManager dm ) { } public void destroyInitiated() { List plugin_interfaces; synchronized( s_plugin_interfaces ){ plugin_interfaces = new ArrayList( s_plugin_interfaces ); } for (int i=0;i<plugin_interfaces.size();i++){ ((PluginInterfaceImpl)plugin_interfaces.get(i)).closedownInitiated(); } if ( default_plugin != null ){ default_plugin.closedownInitiated(); } } public void destroyed() { List plugin_interfaces; synchronized( s_plugin_interfaces ){ plugin_interfaces = new ArrayList( s_plugin_interfaces ); } for (int i=0;i<plugin_interfaces.size();i++){ ((PluginInterfaceImpl)plugin_interfaces.get(i)).closedownComplete(); } if ( default_plugin != null ){ default_plugin.closedownComplete(); } } public void seedingStatusChanged( boolean seeding_only_mode, boolean b ){ /*nothing*/ } protected void runPEVTask( AERunnable run ) { async_dispatcher.dispatch( run ); } protected List<PluginEvent> getPEVHistory() { return( plugin_event_history ); } protected void fireEventSupport( final int type, final Object value ) { async_dispatcher.dispatch( new AERunnable() { public void runSupport() { PluginEvent ev = new PluginEvent() { public int getType() { return( type ); } public Object getValue() { return( value ); } }; if ( type == PluginEvent.PEV_CONFIGURATION_WIZARD_STARTS || type == PluginEvent.PEV_CONFIGURATION_WIZARD_COMPLETES || type == PluginEvent.PEV_INITIAL_SHARING_COMPLETE || type == PluginEvent.PEV_INITIALISATION_UI_COMPLETES || type == PluginEvent.PEV_ALL_PLUGINS_INITIALISED ){ plugin_event_history.add( ev ); if ( plugin_event_history.size() > 1024 ){ Debug.out( "Plugin event history too large!!!!" ); plugin_event_history.remove( 0 ); } } List plugin_interfaces; synchronized( s_plugin_interfaces ){ plugin_interfaces = new ArrayList( s_plugin_interfaces ); } for (int i=0;i<plugin_interfaces.size();i++){ try{ ((PluginInterfaceImpl)plugin_interfaces.get(i)).firePluginEventSupport(ev); }catch(Throwable e ){ Debug.printStackTrace(e); } } if ( default_plugin != null ){ default_plugin.firePluginEventSupport(ev); } } }); } private void waitForEvents() { if ( async_dispatcher.isDispatchThread()){ Debug.out( "Deadlock - recode this monkey boy" ); }else{ final AESemaphore sem = new AESemaphore( "waiter" ); async_dispatcher.dispatch( new AERunnable() { public void runSupport() { sem.release(); } }); if ( !sem.reserve( 10*1000 )){ Debug.out( "Timeout waiting for event dispatch" ); } } } public static void fireEvent( int type ) { singleton.fireEventSupport(type, null); } public static void fireEvent( int type, Object value ) { singleton.fireEventSupport(type, value); } public static void waitForPluginEvents() { singleton.waitForEvents(); } public void initialisationComplete() { initialisation_complete = true; UIManagerImpl.initialisationComplete(); List plugin_interfaces; synchronized( s_plugin_interfaces ){ plugin_interfaces = new ArrayList( s_plugin_interfaces ); } for (int i=0;i<plugin_interfaces.size();i++){ ((PluginInterfaceImpl)plugin_interfaces.get(i)).initialisationComplete(); } // keep this last as there are things out there that rely on the init complete of the // default interface meaning that everything else is complete and informed complete if ( default_plugin != null ){ default_plugin.initialisationComplete(); } } protected boolean isInitialisationComplete() { return( initialisation_complete ); } public static List<PluginInterfaceImpl> getPluginInterfaces() { return singleton.getPluginInterfacesSupport( false ); } private List<PluginInterfaceImpl> getPluginInterfacesSupport( boolean expect_partial_result ) { if ( !expect_partial_result ){ checkPluginsInitialised(); } synchronized( s_plugin_interfaces ){ return( new ArrayList<PluginInterfaceImpl>( s_plugin_interfaces )); } } public PluginInterface[] getPlugins() { return( getPlugins( false )); } public PluginInterface[] getPlugins( boolean expect_partial_result ) { List pis = getPluginInterfacesSupport( expect_partial_result ); PluginInterface[] res = new PluginInterface[pis.size()]; pis.toArray(res); return( res ); } protected PluginManager getPluginManager() { return( plugin_manager ); } protected PluginInterfaceImpl getPluginFromClass( Class cla ) { return( getPluginFromClass( cla.getName())); } protected PluginInterfaceImpl getPluginFromClass( String class_name ) { List plugin_interfaces; synchronized( s_plugin_interfaces ){ plugin_interfaces = new ArrayList( s_plugin_interfaces ); } for (int i=0;i<plugin_interfaces.size();i++){ PluginInterfaceImpl pi = (PluginInterfaceImpl)plugin_interfaces.get(i); if ( pi.getPlugin().getClass().getName().equals( class_name )){ return( pi ); } } // fall back to the loaded but not-yet-initialised list for (int i=0;i<loaded_pi_list.size();i++){ List l = (List)loaded_pi_list.get(i); for (int j=0;j<l.size();j++){ PluginInterfaceImpl pi = (PluginInterfaceImpl)l.get(j); if ( pi.getPlugin().getClass().getName().equals( class_name )){ return( pi ); } } } return( null ); } public void generate( IndentWriter writer ) { writer.println( "Plugins" ); try{ writer.indent(); List plugin_interfaces; synchronized( s_plugin_interfaces ){ plugin_interfaces = new ArrayList( s_plugin_interfaces ); } for (int i=0;i<plugin_interfaces.size();i++){ PluginInterfaceImpl pi = (PluginInterfaceImpl)plugin_interfaces.get(i); pi.generateEvidence( writer ); } }finally{ writer.exdent(); } } protected static void setVerified( PluginInterfaceImpl pi, Plugin plugin, boolean v, boolean bad ) throws PluginException { Object[] existing = (Object[])verified_plugin_holder.setValue( pi, new Object[]{ plugin, v }); if ( existing != null && ( existing[0] != plugin || (Boolean)existing[1] != v )){ throw( new PluginException( "Verified status change not permitted" )); } if ( bad && !DISABLE_PLUGIN_VERIFICATION ){ throw( new RuntimeException( "Plugin verification failed" )); } } public static boolean isVerified( PluginInterface pi, Plugin plugin ) { if ( !( pi instanceof PluginInterfaceImpl )){ return( false ); } VerifiedPluginHolder holder = verified_plugin_holder; if ( holder.getClass() != VerifiedPluginHolder.class ){ Debug.out( "class mismatch" ); return( false ); } if ( DISABLE_PLUGIN_VERIFICATION ){ Debug.out( " **************************** VERIFICATION DISABLED ******************" ); return( true ); } Object[] ver = (Object[])verified_plugin_holder.getValue( pi ); return( ver != null && ver[0] == plugin && (Boolean)ver[1] ); } public static boolean isCoreOrVerifiedPlugin() { Class<?>[] stack = SESecurityManager.getClassContext(); ClassLoader core = PluginInitializer.class.getClassLoader(); PluginInitializer singleton = peekSingleton(); PluginInterface[] pis = singleton==null?new PluginInterface[0]:singleton.getPlugins(); Set<ClassLoader> ok_loaders = new HashSet<ClassLoader>(); ok_loaders.add( core ); for ( Class<?> c: stack ){ ClassLoader cl = c.getClassLoader(); if ( cl != null && !ok_loaders.contains( cl )){ boolean ok = false; for ( PluginInterface pi: pis ){ Plugin plugin = pi.getPlugin(); if ( plugin.getClass().getClassLoader() == cl ){ if ( isVerified( pi, plugin )){ ok_loaders.add( cl ); ok = true; break; } } } if ( !ok ){ Debug.out( "Class " + c.getCanonicalName() + " with loader " + cl + " isn't trusted" ); return( false ); } } } return( true ); } private static final class VerifiedPluginHolder { private static final Object NULL_VALUE = new Object(); private volatile boolean initialised; private AESemaphore request_sem = new AESemaphore( "ValueHolder" ); private List<Object[]> request_queue = new ArrayList<Object[]>(); private VerifiedPluginHolder() { Class[] context = SESecurityManager.getClassContext(); if ( context.length == 0 ){ return; } if ( context[2] != PluginInitializer.class ){ Debug.out( "Illegal operation" ); return; } AEThread2 t = new AEThread2( "PluginVerifier" ) { public void run() { Map<Object,Object> values = new IdentityHashMap<Object,Object>(); while( true ){ request_sem.reserve(); Object[] req; synchronized( request_queue ){ req = request_queue.remove(0); } if ( req[1] == null ){ req[1] = values.get( req[0] ); }else{ Object existing = values.get( req[0] ); if ( req[1] == NULL_VALUE ){ req[1] = existing; values.remove( req[0] ); }else{ if ( existing != null){ req[1] = existing; }else{ values.put( req[0], req[1] ); } } } ((AESemaphore)req[2]).release(); } } }; t.start(); initialised = true; } public Object removeValue( Object key ) { if ( !initialised ){ return( null ); } AESemaphore sem = new AESemaphore( "ValueHolder:remove" ); Object[] request = new Object[]{ key, NULL_VALUE, sem }; synchronized( request_queue ){ request_queue.add( request ); } request_sem.release(); sem.reserve(); return(request[1]); } public Object setValue( Object key, Object value ) { if ( !initialised ){ return( null ); } AESemaphore sem = new AESemaphore( "ValueHolder:set" ); Object[] request = new Object[]{ key, value, sem }; synchronized( request_queue ){ request_queue.add( request ); } request_sem.release(); sem.reserve(); return(request[1]); } public Object getValue( Object key ) { if ( !initialised ){ return( null ); } AESemaphore sem = new AESemaphore( "ValueHolder:get" ); Object[] request = new Object[]{ key, null, sem }; synchronized( request_queue ){ request_queue.add( request ); } request_sem.release(); sem.reserve(); return( request[1] ); } } }