org.parosproxy.paros.extension.ExtensionLoader Maven / Gradle / Ivy
Show all versions of zap Show documentation
/*
*
* Paros and its related class files.
*
* Paros is an HTTP/HTTPS proxy for assessing web application security.
* Copyright (C) 2003-2004 Chinotec Technologies Company
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Clarified Artistic License
* as published by the Free Software Foundation.
*
* 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
* Clarified Artistic License for more details.
*
* You should have received a copy of the Clarified Artistic License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// ZAP: 2011/12/14 Support for extension dependencies
// ZAP: 2012/02/18 Rationalised session handling
// ZAP: 2012/03/15 Reflected the change in the name of the method optionsChanged of
// the class OptionsChangedListener. Changed the method destroyAllExtension() to
// save the configurations of the main http panels and save the configuration file.
// ZAP: 2012/04/23 Reverted the changes of the method destroyAllExtension(),
// now the configurations of the main http panels and the configuration file
// are saved in the method Control.shutdown(boolean).
// ZAP: 2012/04/24 Changed the method destroyAllExtension to catch exceptions.
// ZAP: 2012/04/25 Added the type argument and removed unnecessary cast.
// ZAP: 2012/07/23 Removed parameter from View.getSessionDialog call.
// ZAP: 2012/07/29 Issue 43: added sessionScopeChanged event
// ZAP: 2012/08/01 Issue 332: added support for Modes
// ZAP: 2012/11/30 Issue 425: Added tab index to support quick start tab
// ZAP: 2012/12/27 Added hookPersistentConnectionListener() method.
// ZAP: 2013/01/16 Issue 453: Dynamic loading and unloading of add-ons
// ZAP: 2013/01/25 Added removeExtension(...) method and further helper methods
// to remove listeners, menu items, etc.
// ZAP: 2013/01/25 Refactored hookMenu(). Resolved some Checkstyle issues.
// ZAP: 2013/01/29 Catch Errors thrown by out of date extensions as well as Exceptions
// ZAP: 2013/07/23 Issue 738: Options to hide tabs
// ZAP: 2013/11/16 Issue 807: Error while loading ZAP when Quick Start Tab is closed
// ZAP: 2013/11/16 Issue 845: AbstractPanel added twice to TabbedPanel2 in ExtensionLoader#addTabPanel
// ZAP: 2013/12/03 Issue 934: Handle files on the command line via extension
// ZAP: 2013/12/13 Added support for Full Layout DISPLAY_OPTION_TOP_FULL in the hookView function.
// ZAP: 2014/03/23 Issue 1022: Proxy - Allow to override a proxied message
// ZAP: 2014/03/23 Issue 1090: Do not add pop up menus if target extension is not enabled
// ZAP: 2014/05/20 Issue 1202: Issue with loading addons that did not initialize correctly
// ZAP: 2014/08/14 Catch Exceptions thrown by extensions when stopping them
// ZAP: 2014/08/14 Issue 1309: NullPointerExceptions during a failed uninstallation of an add-on
// ZAP: 2014/10/07 Issue 1357: Hide unused tabs
// ZAP: 2014/10/09 Issue 1359: Added info logging for splash screen
// ZAP: 2014/10/25 Issue 1062: Added scannerhook to be loaded by an active scanner.
// ZAP: 2014/11/11 Issue 1406: Move online menu items to an add-on
// ZAP: 2014/11/21 Reviewed foreach loops and commented startup process for splash screen progress bar
// ZAP: 2015/01/04 Issue 1379: Not all extension's listeners are hooked during add-on installation
// ZAP: 2015/01/19 Remove online menus when removeMenu(View, ExtensionHook) is called.
// ZAP: 2015/01/19 Issue 1510: New Extension.postInit() method to be called once all extensions loaded
// ZAP: 2015/02/09 Issue 1525: Introduce a database interface layer to allow for alternative implementations
// ZAP: 2015/02/10 Issue 1208: Search classes/resources in add-ons declared as dependencies
// ZAP: 2015/04/09 Generify Extension.getExtension(Class) to avoid unnecessary casts
// ZAP: 2015/09/07 Start GUI on EDT
// ZAP: 2016/04/06 Fix layouts' issues
// ZAP: 2016/04/08 Hook ContextDataFactory/ContextPanelFactory
// ZAP: 2016/05/30 Notification of installation status of the add-ons
// ZAP: 2016/05/30 Issue 2494: ZAP Proxy is not showing the HTTP CONNECT Request in history tab
// ZAP: 2016/08/18 Hook ApiImplementor
// ZAP: 2016/11/23 Call postInit() when starting an extension, startLifeCycle(Extension).
// ZAP: 2017/02/19 Hook/remove extensions' components to/from the main tool bar.
// ZAP: 2017/06/07 Allow to notify of changes in the session's properties (e.g. name, description).
// ZAP: 2017/07/25 Hook HttpSenderListener.
// ZAP: 2017/10/11 Include add-on in extensions' initialisation errors.
// ZAP: 2017/10/31 Add JavaDoc to ExtensionLoader.getExtension(String).
package org.parosproxy.paros.extension;
import java.awt.Component;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.parosproxy.paros.CommandLine;
import org.parosproxy.paros.common.AbstractParam;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.control.Control.Mode;
import org.parosproxy.paros.control.Proxy;
import org.parosproxy.paros.core.proxy.ConnectRequestProxyListener;
import org.parosproxy.paros.core.proxy.OverrideMessageProxyListener;
import org.parosproxy.paros.core.proxy.ProxyListener;
import org.parosproxy.paros.core.scanner.Scanner;
import org.parosproxy.paros.core.scanner.ScannerHook;
import org.parosproxy.paros.db.Database;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.db.DatabaseUnsupportedException;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.OptionsParam;
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.network.HttpSender;
import org.parosproxy.paros.view.AbstractParamDialog;
import org.parosproxy.paros.view.AbstractParamPanel;
import org.parosproxy.paros.view.MainMenuBar;
import org.parosproxy.paros.view.SiteMapPanel;
import org.parosproxy.paros.view.View;
import org.parosproxy.paros.view.WorkbenchPanel;
import org.zaproxy.zap.PersistentConnectionListener;
import org.zaproxy.zap.control.AddOn;
import org.zaproxy.zap.extension.AddonFilesChangedListener;
import org.zaproxy.zap.extension.api.API;
import org.zaproxy.zap.extension.api.ApiImplementor;
import org.zaproxy.zap.extension.AddOnInstallationStatusListener;
import org.zaproxy.zap.model.ContextDataFactory;
import org.zaproxy.zap.network.HttpSenderListener;
import org.zaproxy.zap.view.ContextPanelFactory;
import org.zaproxy.zap.view.MainToolbarPanel;
import org.zaproxy.zap.view.SiteMapListener;
public class ExtensionLoader {
private final List extensionList = new ArrayList<>();
private final Map, Extension> extensionsMap = new HashMap<>();
private final Map extensionHooks = new HashMap<>();
private Model model = null;
private View view = null;
private static final Logger logger = Logger.getLogger(ExtensionLoader.class);
public ExtensionLoader(Model model, View view) {
this.model = model;
this.view = view;
}
public void addExtension(Extension extension) {
extensionList.add(extension);
extensionsMap.put(extension.getClass(), extension);
}
public void destroyAllExtension() {
for (int i = 0; i < getExtensionCount(); i++) {
try {
getExtension(i).destroy();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
public Extension getExtension(int i) {
return extensionList.get(i);
}
/**
* Gets the {@code Extension} with the given name.
*
* @param name the name of the {@code Extension}.
* @return the {@code Extension} or {@code null} if not found/enabled.
* @see #getExtension(Class)
*/
public Extension getExtension(String name) {
if (name != null) {
for (int i = 0; i < extensionList.size(); i++) {
Extension p = getExtension(i);
if (p.getName().equalsIgnoreCase(name)) {
return p;
}
}
}
return null;
}
public Extension getExtensionByClassName(String name) {
if (name != null) {
for (int i = 0; i < extensionList.size(); i++) {
Extension p = getExtension(i);
if (p.getClass().getName().equals(name)) {
return p;
}
}
}
return null;
}
/**
* Gets the {@code Extension} with the given class.
*
* @param clazz the class of the {@code Extension}
* @return the {@code Extension} or {@code null} if not found/enabled.
*/
public T getExtension(Class clazz) {
if (clazz != null) {
Extension extension = extensionsMap.get(clazz);
if (extension != null) {
return clazz.cast(extension);
}
}
return null;
}
/**
* Tells whether or not an {@code Extension} with the given
* {@code extensionName} is enabled.
*
* @param extensionName the name of the extension
* @return {@code true} if the extension is enabled, {@code false}
* otherwise.
* @throws IllegalArgumentException if the {@code extensionName} is
* {@code null}.
* @see #getExtension(String)
* @see Extension
*/
public boolean isExtensionEnabled(String extensionName) {
if (extensionName == null) {
throw new IllegalArgumentException("Parameter extensionName must not be null.");
}
Extension extension = getExtension(extensionName);
if (extension == null) {
return false;
}
return extension.isEnabled();
}
public int getExtensionCount() {
return extensionList.size();
}
public void hookProxyListener(Proxy proxy) {
for (ExtensionHook hook : extensionHooks.values()) {
hookProxyListeners(proxy, hook.getProxyListenerList());
}
}
private static void hookProxyListeners(Proxy proxy, List listeners) {
for (ProxyListener listener : listeners) {
try {
if (listener != null) {
proxy.addProxyListener(listener);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
private void removeProxyListener(ExtensionHook hook) {
Proxy proxy = Control.getSingleton().getProxy();
List listenerList = hook.getProxyListenerList();
for (ProxyListener listener : listenerList) {
try {
if (listener != null) {
proxy.removeProxyListener(listener);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
public void hookOverrideMessageProxyListener(Proxy proxy) {
for (ExtensionHook hook : extensionHooks.values()) {
List listenerList = hook.getOverrideMessageProxyListenerList();
for (OverrideMessageProxyListener listener : listenerList) {
try {
if (listener != null) {
proxy.addOverrideMessageProxyListener(listener);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
private void removeOverrideMessageProxyListener(ExtensionHook hook) {
Proxy proxy = Control.getSingleton().getProxy();
List listenerList = hook.getOverrideMessageProxyListenerList();
for (OverrideMessageProxyListener listener : listenerList) {
try {
if (listener != null) {
proxy.removeOverrideMessageProxyListener(listener);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
/**
* Hooks (adds) the {@code ConnectRequestProxyListener}s of the loaded extensions to the given {@code proxy}.
*
* Note: even if public this method is expected to be called only by core classes (for example,
* {@code Control}).
*
* @param proxy the local proxy
* @since 2.5.0
*/
public void hookConnectRequestProxyListeners(Proxy proxy) {
for (ExtensionHook hook : extensionHooks.values()) {
hookConnectRequestProxyListeners(proxy, hook.getConnectRequestProxyListeners());
}
}
private static void hookConnectRequestProxyListeners(Proxy proxy, List listeners) {
for (ConnectRequestProxyListener listener : listeners) {
proxy.addConnectRequestProxyListener(listener);
}
}
private void removeConnectRequestProxyListener(ExtensionHook hook) {
Proxy proxy = Control.getSingleton().getProxy();
for (ConnectRequestProxyListener listener : hook.getConnectRequestProxyListeners()) {
proxy.removeConnectRequestProxyListener(listener);
}
}
public void hookPersistentConnectionListener(Proxy proxy) {
for (ExtensionHook hook : extensionHooks.values()) {
hookPersistentConnectionListeners(proxy, hook.getPersistentConnectionListener());
}
}
private static void hookPersistentConnectionListeners(Proxy proxy, List listeners) {
for (PersistentConnectionListener listener : listeners) {
try {
if (listener != null) {
proxy.addPersistentConnectionListener(listener);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
private void removePersistentConnectionListener(ExtensionHook hook) {
Proxy proxy = Control.getSingleton().getProxy();
List listenerList = hook.getPersistentConnectionListener();
for (PersistentConnectionListener listener : listenerList) {
try {
if (listener != null) {
proxy.removePersistentConnectionListener(listener);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
// ZAP: Added support for site map listeners
public void hookSiteMapListener(SiteMapPanel siteMapPanel) {
for (ExtensionHook hook : extensionHooks.values()) {
hookSiteMapListeners(siteMapPanel, hook.getSiteMapListenerList());
}
}
private static void hookSiteMapListeners(SiteMapPanel siteMapPanel, List listeners) {
for (SiteMapListener listener : listeners) {
try {
if (listener != null) {
siteMapPanel.addSiteMapListener(listener);
}
} catch (Exception e) {
// ZAP: Log the exception
logger.error(e.getMessage(), e);
}
}
}
private void removeSiteMapListener(ExtensionHook hook) {
if (view != null) {
SiteMapPanel siteMapPanel = view.getSiteTreePanel();
List listenerList = hook.getSiteMapListenerList();
for (SiteMapListener listener : listenerList) {
try {
if (listener != null) {
siteMapPanel.removeSiteMapListener(listener);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
// ZAP: method called by the scanner to load all scanner hooks.
public void hookScannerHook(Scanner scan) {
Iterator iter = extensionHooks.values().iterator();
while (iter.hasNext()) {
ExtensionHook hook = iter.next();
List scannerHookList = hook.getScannerHookList();
for (ScannerHook scannerHook : scannerHookList) {
try {
if (hook != null) {
scan.addScannerHook(scannerHook);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
public void optionsChangedAllPlugin(OptionsParam options) {
for (ExtensionHook hook : extensionHooks.values()) {
List listenerList = hook.getOptionsChangedListenerList();
for (OptionsChangedListener listener : listenerList) {
try {
if (listener != null) {
listener.optionsChanged(options);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
public void runCommandLine() {
Extension ext;
for (int i = 0; i < getExtensionCount(); i++) {
ext = getExtension(i);
if (ext instanceof CommandLineListener) {
CommandLineListener listener = (CommandLineListener) ext;
listener.execute(extensionHooks.get(ext).getCommandLineArgument());
}
}
}
public void sessionChangedAllPlugin(Session session) {
logger.debug("sessionChangedAllPlugin");
for (ExtensionHook hook : extensionHooks.values()) {
List listenerList = hook.getSessionListenerList();
for (SessionChangedListener listener : listenerList) {
try {
if (listener != null) {
listener.sessionChanged(session);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
public void databaseOpen(Database db) {
Extension ext;
for (int i = 0; i < getExtensionCount(); i++) {
ext = getExtension(i);
try {
ext.databaseOpen(db);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
public void sessionAboutToChangeAllPlugin(Session session) {
logger.debug("sessionAboutToChangeAllPlugin");
for (ExtensionHook hook : extensionHooks.values()) {
List listenerList = hook.getSessionListenerList();
for (SessionChangedListener listener : listenerList) {
try {
if (listener != null) {
listener.sessionAboutToChange(session);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
public void sessionScopeChangedAllPlugin(Session session) {
logger.debug("sessionScopeChangedAllPlugin");
for (ExtensionHook hook : extensionHooks.values()) {
List listenerList = hook.getSessionListenerList();
for (SessionChangedListener listener : listenerList) {
try {
if (listener != null) {
listener.sessionScopeChanged(session);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
public void sessionModeChangedAllPlugin(Mode mode) {
logger.debug("sessionModeChangedAllPlugin");
for (ExtensionHook hook : extensionHooks.values()) {
List listenerList = hook.getSessionListenerList();
for (SessionChangedListener listener : listenerList) {
try {
if (listener != null) {
listener.sessionModeChanged(mode);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
/**
* Notifies that the properties (e.g. name, description) of the current session were changed.
*
* Should be called only by "core" classes.
*
* @param session the session changed.
* @since 2.7.0
*/
public void sessionPropertiesChangedAllPlugin(Session session) {
logger.debug("sessionPropertiesChangedAllPlugin");
for (ExtensionHook hook : extensionHooks.values()) {
for (SessionChangedListener listener : hook.getSessionListenerList()) {
try {
if (listener != null) {
listener.sessionPropertiesChanged(session);
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
public void addonFilesAdded() {
for (ExtensionHook hook : extensionHooks.values()) {
List listenerList = hook.getAddonFilesChangedListener();
for (AddonFilesChangedListener listener : listenerList) {
try {
listener.filesAdded();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
public void addonFilesRemoved() {
for (ExtensionHook hook : extensionHooks.values()) {
List listenerList = hook.getAddonFilesChangedListener();
for (AddonFilesChangedListener listener : listenerList) {
try {
listener.filesRemoved();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
}
/**
* Notifies {@code Extension}s' {@code AddOnInstallationStatusListener}s that the given add-on was installed.
*
* @param addOn the add-on that was installed, must not be {@code null}
* @since 2.5.0
*/
public void addOnInstalled(AddOn addOn) {
for (ExtensionHook hook : extensionHooks.values()) {
for (AddOnInstallationStatusListener listener : hook.getAddOnInstallationStatusListeners()) {
try {
listener.addOnInstalled(addOn);
} catch (Exception e) {
logger.error("An error occurred while notifying: " + listener.getClass().getCanonicalName(), e);
}
}
}
}
/**
* Notifies {@code Extension}s' {@code AddOnInstallationStatusListener}s that the given add-on was soft uninstalled.
*
* @param addOn the add-on that was soft uninstalled, must not be {@code null}
* @param successfully if the soft uninstallation was successful, that is, no errors occurred while uninstalling it
* @since 2.5.0
*/
public void addOnSoftUninstalled(AddOn addOn, boolean successfully) {
for (ExtensionHook hook : extensionHooks.values()) {
for (AddOnInstallationStatusListener listener : hook.getAddOnInstallationStatusListeners()) {
try {
listener.addOnSoftUninstalled(addOn, successfully);
} catch (Exception e) {
logger.error("An error occurred while notifying: " + listener.getClass().getCanonicalName(), e);
}
}
}
}
/**
* Notifies {@code Extension}s' {@code AddOnInstallationStatusListener}s that the given add-on was uninstalled.
*
* @param addOn the add-on that was uninstalled, must not be {@code null}
* @param successfully if the uninstallation was successful, that is, no errors occurred while uninstalling it
* @since 2.5.0
*/
public void addOnUninstalled(AddOn addOn, boolean successfully) {
for (ExtensionHook hook : extensionHooks.values()) {
for (AddOnInstallationStatusListener listener : hook.getAddOnInstallationStatusListeners()) {
try {
listener.addOnUninstalled(addOn, successfully);
} catch (Exception e) {
logger.error("An error occurred while notifying: " + listener.getClass().getCanonicalName(), e);
}
}
}
}
public void startAllExtension(double progressFactor) {
double factorPerc = progressFactor / getExtensionCount();
for (int i = 0; i < getExtensionCount(); i++) {
Extension extension = getExtension(i);
try {
extension.start();
if (view != null) {
view.addSplashScreenLoadingCompletion(factorPerc);
}
} catch (Exception e) {
logExtensionInitError(extension, e);
}
}
}
/**
* Initialize and start all Extensions
* This function loops for all getExtensionCount() exts
* launching each specific initialization element (model, xml, view, hook, etc.)
*/
public void startLifeCycle() {
// Percentages are passed into the calls as doubles
if (view != null) {
view.setSplashScreenLoadingCompletion(0.0);
}
// Step 3: initialize all (slow)
initAllExtension(5.0);
// Step 4: initialize models (quick)
initModelAllExtension(model, 0.0);
// Step 5: initialize xmls (quick)
initXMLAllExtension(model.getSession(), model.getOptionsParam(), 0.0);
// Step 6: initialize viewes (slow)
initViewAllExtension(view, 10.0);
// Step 7: initialize hooks (slowest)
hookAllExtension(75.0);
// Step 8: start all extensions(quick)
startAllExtension(10.0);
}
/**
* Initialize a specific Extension
* @param ext the Extension that need to be initialized
* @throws DatabaseUnsupportedException
* @throws DatabaseException
*/
public void startLifeCycle(Extension ext) throws DatabaseException, DatabaseUnsupportedException {
ext.init();
ext.databaseOpen(model.getDb());
ext.initModel(model);
ext.initXML(model.getSession(), model.getOptionsParam());
ext.initView(view);
ExtensionHook extHook = new ExtensionHook(model, view);
try {
ext.hook(extHook);
extensionHooks.put(ext, extHook);
hookContextDataFactories(ext, extHook);
hookApiImplementors(ext, extHook);
if (view != null) {
// no need to hook view if no GUI
hookView(ext, view, extHook);
hookMenu(view, extHook);
}
hookOptions(extHook);
ext.optionsLoaded();
ext.postInit();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
ext.start();
Proxy proxy = Control.getSingleton().getProxy();
hookProxyListeners(proxy, extHook.getProxyListenerList());
hookPersistentConnectionListeners(proxy, extHook.getPersistentConnectionListener());
hookConnectRequestProxyListeners(proxy, extHook.getConnectRequestProxyListeners());
if (view != null) {
hookSiteMapListeners(view.getSiteTreePanel(), extHook.getSiteMapListenerList());
}
}
public void stopAllExtension() {
for (int i = 0; i < getExtensionCount(); i++) {
try {
getExtension(i).stop();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
// ZAP: Added the type argument.
private void addParamPanel(List panelList, AbstractParamDialog dialog) {
String[] ROOT = {};
for (AbstractParamPanel panel : panelList) {
try {
dialog.addParamPanel(ROOT, panel, true);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
private void removeParamPanel(List panelList, AbstractParamDialog dialog) {
for (AbstractParamPanel panel : panelList) {
try {
dialog.removeParamPanel(panel);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
dialog.revalidate();
}
private void hookAllExtension(double progressFactor) {
final double factorPerc = progressFactor / getExtensionCount();
for (int i = 0; i < getExtensionCount(); i++) {
final Extension ext = getExtension(i);
try {
logger.info("Initializing " + ext.getDescription());
final ExtensionHook extHook = new ExtensionHook(model, view);
ext.hook(extHook);
extensionHooks.put(ext, extHook);
hookContextDataFactories(ext, extHook);
hookApiImplementors(ext, extHook);
hookHttpSenderListeners(ext, extHook);
if (view != null) {
EventQueue.invokeAndWait(new Runnable() {
@Override
public void run() {
// no need to hook view if no GUI
hookView(ext, view, extHook);
hookMenu(view, extHook);
view.addSplashScreenLoadingCompletion(factorPerc);
}
});
}
hookOptions(extHook);
ext.optionsLoaded();
} catch (Throwable e) {
// Catch Errors thrown by out of date extensions as well as Exceptions
logExtensionInitError(ext, e);
}
}
// Call postInit for all extensions after they have all been initialized
for (int i = 0; i < getExtensionCount(); i++) {
Extension extension = getExtension(i);
try {
extension.postInit();
} catch (Throwable e) {
// Catch Errors thrown by out of date extensions as well as Exceptions
logExtensionInitError(extension, e);
}
}
if (view != null) {
view.getMainFrame().getMainMenuBar().validate();
view.getMainFrame().validate();
}
}
private static void logExtensionInitError(Extension extension, Throwable e) {
StringBuilder strBuilder = new StringBuilder(150);
strBuilder.append("Failed to initialise extension ");
strBuilder.append(extension.getClass().getCanonicalName());
AddOn addOn = extension.getAddOn();
if (addOn != null) {
strBuilder.append(" (from add-on ").append(addOn).append(')');
}
strBuilder.append(", cause: ");
strBuilder.append(ExceptionUtils.getRootCauseMessage(e));
logger.error(strBuilder.toString(), e);
}
private void hookContextDataFactories(Extension extension, ExtensionHook extHook) {
for (ContextDataFactory contextDataFactory : extHook.getContextDataFactories()) {
try {
model.addContextDataFactory(contextDataFactory);
} catch (Exception e) {
logger.error("Error while adding a ContextDataFactory from " + extension.getClass().getCanonicalName(), e);
}
}
}
private void hookApiImplementors(Extension extension, ExtensionHook extHook) {
for (ApiImplementor apiImplementor : extHook.getApiImplementors()) {
try {
API.getInstance().registerApiImplementor(apiImplementor);
} catch (Exception e) {
logger.error("Error while adding an ApiImplementor from " + extension.getClass().getCanonicalName(), e);
}
}
}
private void hookHttpSenderListeners(Extension extension, ExtensionHook extHook) {
for (HttpSenderListener httpSenderListener : extHook.getHttpSenderListeners()) {
try {
HttpSender.addListener(httpSenderListener);
} catch (Exception e) {
logger.error("Error while adding an HttpSenderListener from " + extension.getClass().getCanonicalName(), e);
}
}
}
/**
* Hook command line listener with the command line processor
*
* @param cmdLine
* @throws java.lang.Exception
*/
public void hookCommandLineListener(CommandLine cmdLine) throws Exception {
List allCommandLineList = new ArrayList<>();
Map extMap = new HashMap<>();
for (Map.Entry entry : extensionHooks.entrySet()) {
ExtensionHook hook = entry.getValue();
CommandLineArgument[] arg = hook.getCommandLineArgument();
if (arg.length > 0) {
allCommandLineList.add(arg);
}
Extension extension = entry.getKey();
if (extension instanceof CommandLineListener) {
CommandLineListener cli = (CommandLineListener) extension;
List exts = cli.getHandledExtensions();
if (exts != null) {
for (String ext : exts) {
extMap.put(ext, cli);
}
}
}
}
cmdLine.parse(allCommandLineList, extMap);
}
private void hookMenu(View view, ExtensionHook hook) {
if (view == null) {
return;
}
ExtensionHookMenu hookMenu = hook.getHookMenu();
if (hookMenu == null) {
return;
}
MainMenuBar menuBar = view.getMainFrame().getMainMenuBar();
// 2 menus at the back (Tools/Help)
addMenuHelper(menuBar, hookMenu.getNewMenus(), 2);
addMenuHelper(menuBar.getMenuFile(), hookMenu.getFile(), 2);
addMenuHelper(menuBar.getMenuTools(), hookMenu.getTools(), 2);
addMenuHelper(menuBar.getMenuEdit(), hookMenu.getEdit());
addMenuHelper(menuBar.getMenuView(), hookMenu.getView());
addMenuHelper(menuBar.getMenuAnalyse(), hookMenu.getAnalyse());
addMenuHelper(menuBar.getMenuHelp(), hookMenu.getHelpMenus());
addMenuHelper(menuBar.getMenuReport(), hookMenu.getReportMenus());
addMenuHelper(menuBar.getMenuOnline(), hookMenu.getOnlineMenus());
addMenuHelper(view.getPopupList(), hookMenu.getPopupMenus());
}
private void addMenuHelper(JMenu menu, List items) {
addMenuHelper(menu, items, 0);
}
private void addMenuHelper(JMenuBar menuBar, List items, int existingCount) {
for (JMenuItem item : items) {
if (item != null) {
menuBar.add(item, menuBar.getMenuCount() - existingCount);
}
}
menuBar.revalidate();
}
private void addMenuHelper(JMenu menu, List items, int existingCount) {
for (JMenuItem item : items) {
if (item != null) {
if (item == ExtensionHookMenu.MENU_SEPARATOR) {
menu.addSeparator();
continue;
}
menu.add(item, menu.getItemCount() - existingCount);
}
}
menu.revalidate();
}
private void addMenuHelper(List menuList, List items) {
for (JMenuItem item : items) {
if (item != null) {
menuList.add(item);
}
}
}
private void removeMenu(View view, ExtensionHook hook) {
if (view == null) {
return;
}
ExtensionHookMenu hookMenu = hook.getHookMenu();
if (hookMenu == null) {
return;
}
MainMenuBar menuBar = view.getMainFrame().getMainMenuBar();
// clear up various menus
removeMenuHelper(menuBar, hookMenu.getNewMenus());
removeMenuHelper(menuBar.getMenuFile(), hookMenu.getFile());
removeMenuHelper(menuBar.getMenuTools(), hookMenu.getTools());
removeMenuHelper(menuBar.getMenuEdit(), hookMenu.getEdit());
removeMenuHelper(menuBar.getMenuView(), hookMenu.getView());
removeMenuHelper(menuBar.getMenuAnalyse(), hookMenu.getAnalyse());
removeMenuHelper(menuBar.getMenuHelp(), hookMenu.getHelpMenus());
removeMenuHelper(menuBar.getMenuReport(), hookMenu.getReportMenus());
removeMenuHelper(menuBar.getMenuOnline(), hookMenu.getOnlineMenus());
removeMenuHelper(view.getPopupList(), hookMenu.getPopupMenus());
view.refreshTabViewMenus();
}
private void removeMenuHelper(JMenuBar menuBar, List items) {
for (JMenuItem item : items) {
if (item != null) {
menuBar.remove(item);
}
}
menuBar.revalidate();
}
private void removeMenuHelper(JMenu menu, List items) {
for (JMenuItem item : items) {
if (item != null) {
menu.remove(item);
}
}
menu.revalidate();
}
private void removeMenuHelper(List menuList, List items) {
for (JMenuItem item : items) {
if (item != null) {
menuList.remove(item);
}
}
}
private void hookOptions(ExtensionHook hook) {
List list = hook.getOptionsParamSetList();
for (AbstractParam paramSet : list) {
try {
model.getOptionsParam().addParamSet(paramSet);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
private void unloadOptions(ExtensionHook hook) {
List list = hook.getOptionsParamSetList();
for (AbstractParam paramSet : list) {
try {
model.getOptionsParam().removeParamSet(paramSet);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
private void hookView(Extension extension, View view, ExtensionHook hook) {
if (view == null) {
return;
}
ExtensionHookView pv = hook.getHookView();
if (pv == null) {
return;
}
for (ContextPanelFactory contextPanelFactory : pv.getContextPanelFactories()) {
try {
view.addContextPanelFactory(contextPanelFactory);
} catch (Exception e) {
logger.error("Error while adding a ContextPanelFactory from " + extension.getClass().getCanonicalName(), e);
}
}
MainToolbarPanel mainToolBarPanel = view.getMainFrame().getMainToolbarPanel();
for (Component component : pv.getMainToolBarComponents()) {
try {
mainToolBarPanel.addToolBarComponent(component);
} catch (Exception e) {
logger.error("Error while adding a component to the main tool bar panel, from " +
extension.getClass().getCanonicalName(), e);
}
}
view.getWorkbench().addPanels(pv.getSelectPanel(), WorkbenchPanel.PanelType.SELECT);
view.getWorkbench().addPanels(pv.getWorkPanel(), WorkbenchPanel.PanelType.WORK);
view.getWorkbench().addPanels(pv.getStatusPanel(), WorkbenchPanel.PanelType.STATUS);
addParamPanel(pv.getSessionPanel(), view.getSessionDialog());
addParamPanel(pv.getOptionsPanel(), view.getOptionsDialog(""));
}
private void removeView(Extension extension, View view, ExtensionHook hook) {
if (view == null) {
return;
}
ExtensionHookView pv = hook.getHookView();
if (pv == null) {
return;
}
for (ContextPanelFactory contextPanelFactory : pv.getContextPanelFactories()) {
try {
view.removeContextPanelFactory(contextPanelFactory);
} catch (Exception e) {
logger.error("Error while removing a ContextPanelFactory from " + extension.getClass().getCanonicalName(), e);
}
}
MainToolbarPanel mainToolBarPanel = view.getMainFrame().getMainToolbarPanel();
for (Component component : pv.getMainToolBarComponents()) {
try {
mainToolBarPanel.removeToolBarComponent(component);
} catch (Exception e) {
logger.error("Error while removing a component from the main tool bar panel, from "
+ extension.getClass().getCanonicalName(), e);
}
}
view.getWorkbench().removePanels(pv.getSelectPanel(), WorkbenchPanel.PanelType.SELECT);
view.getWorkbench().removePanels(pv.getWorkPanel(), WorkbenchPanel.PanelType.WORK);
view.getWorkbench().removePanels(pv.getStatusPanel(), WorkbenchPanel.PanelType.STATUS);
removeParamPanel(pv.getSessionPanel(), view.getSessionDialog());
removeParamPanel(pv.getOptionsPanel(), view.getOptionsDialog(""));
}
public void removeStatusPanel(AbstractPanel panel) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getWorkbench().removePanel(panel, WorkbenchPanel.PanelType.STATUS);
}
public void removeOptionsPanel(AbstractParamPanel panel) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getOptionsDialog("").removeParamPanel(panel);
}
public void removeOptionsParamSet(AbstractParam params) {
model.getOptionsParam().removeParamSet(params);
}
public void removeWorkPanel(AbstractPanel panel) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getWorkbench().removePanel(panel, WorkbenchPanel.PanelType.WORK);
}
public void removePopupMenuItem(ExtensionPopupMenuItem popupMenuItem) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getPopupList().remove(popupMenuItem);
}
public void removeFileMenuItem(JMenuItem menuItem) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getMainFrame().getMainMenuBar().getMenuFile().remove(menuItem);
}
public void removeEditMenuItem(JMenuItem menuItem) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getMainFrame().getMainMenuBar().getMenuEdit().remove(menuItem);
}
public void removeViewMenuItem(JMenuItem menuItem) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getMainFrame().getMainMenuBar().getMenuView().remove(menuItem);
}
public void removeToolsMenuItem(JMenuItem menuItem) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getMainFrame().getMainMenuBar().getMenuTools().remove(menuItem);
}
public void removeHelpMenuItem(JMenuItem menuItem) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getMainFrame().getMainMenuBar().getMenuHelp().remove(menuItem);
}
public void removeReportMenuItem(JMenuItem menuItem) {
if (!View.isInitialised()) {
return;
}
View.getSingleton().getMainFrame().getMainMenuBar().getMenuReport().remove(menuItem);
}
/**
* Init all extensions
*/
private void initAllExtension(double progressFactor) {
double factorPerc = progressFactor / getExtensionCount();
for (int i = 0; i < getExtensionCount(); i++) {
Extension extension = getExtension(i);
try {
extension.init();
extension.databaseOpen(Model.getSingleton().getDb());
if (view != null) {
view.addSplashScreenLoadingCompletion(factorPerc);
}
} catch (Throwable e) {
logExtensionInitError(extension, e);
}
}
}
/**
* Init all extensions with the same Model
* @param model the model to apply to all extensions
*/
private void initModelAllExtension(Model model, double progressFactor) {
double factorPerc = progressFactor / getExtensionCount();
for (int i = 0; i < getExtensionCount(); i++) {
Extension extension = getExtension(i);
try {
extension.initModel(model);
if (view != null) {
view.addSplashScreenLoadingCompletion(factorPerc);
}
} catch (Exception e) {
logExtensionInitError(extension, e);
}
}
}
/**
* Init all extensions with the same View
* @param view the View that need to be applied
*/
private void initViewAllExtension(final View view, double progressFactor) {
if (view == null) {
return;
}
final double factorPerc = progressFactor / getExtensionCount();
for (int i = 0; i < getExtensionCount(); i++) {
final Extension extension = getExtension(i);
try {
EventQueue.invokeAndWait(new Runnable() {
@Override
public void run() {
extension.initView(view);
view.addSplashScreenLoadingCompletion(factorPerc);
}
});
} catch (Exception e) {
logExtensionInitError(extension, e);
}
}
}
private void initXMLAllExtension(Session session, OptionsParam options, double progressFactor) {
double factorPerc = progressFactor / getExtensionCount();
for (int i = 0; i < getExtensionCount(); i++) {
Extension extension = getExtension(i);
try {
extension.initXML(session, options);
if (view != null) {
view.addSplashScreenLoadingCompletion(factorPerc);
}
} catch (Exception e) {
logExtensionInitError(extension, e);
}
}
}
/**
* Removes an extension from internal list. As a result listeners added via
* the {@link ExtensionHook} object are unregistered.
*
* @param extension
* @param hook
*/
public void removeExtension(Extension extension, ExtensionHook hook) {
extensionList.remove(extension);
extensionsMap.remove(extension.getClass());
if (hook == null) {
logger.info("ExtensionHook is null for \"" + extension.getClass().getCanonicalName()
+ "\" the hooked objects will not be automatically removed.");
return;
}
// by removing the ExtensionHook object,
// the following listeners are no longer informed:
// * SessionListeners
// * OptionsChangedListeners
extensionHooks.values().remove(hook);
unloadOptions(hook);
removePersistentConnectionListener(hook);
removeProxyListener(hook);
removeOverrideMessageProxyListener(hook);
removeConnectRequestProxyListener(hook);
removeSiteMapListener(hook);
for (ContextDataFactory contextDataFactory : hook.getContextDataFactories()) {
try {
model.removeContextDataFactory(contextDataFactory);
} catch (Exception e) {
logger.error("Error while removing a ContextDataFactory from " + extension.getClass().getCanonicalName(), e);
}
}
for (ApiImplementor apiImplementor : hook.getApiImplementors()) {
try {
API.getInstance().removeApiImplementor(apiImplementor);
} catch (Exception e) {
logger.error("Error while removing an ApiImplementor from " + extension.getClass().getCanonicalName(), e);
}
}
for (HttpSenderListener httpSenderListener : hook.getHttpSenderListeners()) {
try {
HttpSender.removeListener(httpSenderListener);
} catch (Exception e) {
logger.error("Error while removing an HttpSenderListener from " + extension.getClass().getCanonicalName(), e);
}
}
removeViewInEDT(extension, hook);
}
private void removeViewInEDT(final Extension extension, final ExtensionHook hook) {
if (view == null) {
return;
}
if (EventQueue.isDispatchThread()) {
removeView(extension, view, hook);
removeMenu(view, hook);
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
removeViewInEDT(extension, hook);
}
});
}
}
/**
* Gets the names of all unsaved resources of all the extensions.
*
* @return a {@code List} containing all the unsaved resources of all add-ons, never {@code null}
* @see Extension#getActiveActions()
*/
public List getUnsavedResources() {
List list = new ArrayList<>();
List l;
for (int i = 0; i < getExtensionCount(); i++) {
l = getExtension(i).getUnsavedResources();
if (l != null) {
list.addAll(l);
}
}
return list;
}
/**
* Gets the names of all active actions of all the extensions.
*
* @return a {@code List} containing all the active actions of all add-ons, never {@code null}
* @since 2.4.0
* @see Extension#getActiveActions()
*/
public List getActiveActions() {
List list = new ArrayList<>();
List l;
for (int i = 0; i < getExtensionCount(); i++) {
l = getExtension(i).getActiveActions();
if (l != null) {
list.addAll(l);
}
}
return list;
}
}