/******************************************************************************* * Copyright (c) 2009, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.debug.ui; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.eclipse.core.internal.runtime.AdapterManager; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.*; import org.eclipse.debug.core.model.*; import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer; import org.eclipse.debug.internal.ui.views.launch.LaunchView; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugModelPresentation; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.equinox.security.storage.ISecurePreferences; import org.eclipse.equinox.security.storage.SecurePreferencesFactory; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.php.internal.debug.core.IPHPDebugConstants; import org.eclipse.php.internal.debug.core.PHPDebugPlugin; import org.eclipse.php.internal.debug.core.launching.PHPLaunch; import org.eclipse.php.internal.debug.core.launching.PHPLaunchUtilities; import org.eclipse.php.internal.debug.core.model.IPHPDebugTarget; import org.eclipse.php.internal.debug.core.xdebug.dbgp.model.DBGpMultiSessionTarget; import org.eclipse.php.internal.debug.core.zend.model.PHPMultiDebugTarget; import org.eclipse.php.internal.debug.ui.actions.EvaluationContextManager; import org.eclipse.php.internal.debug.ui.views.DebugBrowserView; import org.eclipse.php.internal.debug.ui.views.DebugOutputView; import org.eclipse.php.internal.debug.ui.views.variables.PHPDebugElementAdapterFactory; import org.eclipse.php.internal.ui.util.ImageDescriptorRegistry; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.*; import org.eclipse.ui.console.IConsoleConstants; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; /** * The main plugin class to be used in the desktop. */ @SuppressWarnings("restriction") public class PHPDebugUIPlugin extends AbstractUIPlugin { // The shared instance. private static PHPDebugUIPlugin plugin; private ImageDescriptorRegistry fImageDescriptorRegistry; public static final String ID = "org.eclipse.php.debug.ui"; //$NON-NLS-1$ public static final int INTERNAL_ERROR = 10001; private ShowViewListener showViewListener; private TerminateDebugLaunchListener finishDebugLaunchListener; private FirstSelectionDebugLaunchListener firstSelectionDebugLaunchListener; private IDebugModelPresentation fUtilPresentation; /** * The constructor. */ public PHPDebugUIPlugin() { plugin = this; } /** * This method is called upon plug-in activation */ @SuppressWarnings("unchecked") public void start(BundleContext context) throws Exception { super.start(context); showViewListener = new ShowViewListener(); DebugPlugin.getDefault().addDebugEventListener(showViewListener); // Install the TerminateDebugLaunchListener, which is responsible of // asking // the user to return to the PHP perspective when all the debug sessions // where terminated. finishDebugLaunchListener = new TerminateDebugLaunchListener(); DebugPlugin.getDefault().getLaunchManager().addLaunchListener(finishDebugLaunchListener); // Install the FirstSelectionDebugLaunchListener, which is responsible // of selecting the // new launch in the LaunchView. firstSelectionDebugLaunchListener = new FirstSelectionDebugLaunchListener(); DebugPlugin.getDefault().getLaunchManager().addLaunchListener(firstSelectionDebugLaunchListener); // Register the PHPDebugElementAdapterFactory. // To make sure we are the first adapter factory for the IVariable // class, we insert the // factory before any other factory. AdapterManager manager = (AdapterManager) Platform.getAdapterManager(); List<IAdapterFactory> list = (List<IAdapterFactory>) manager.getFactories().get(IVariable.class.getName()); PHPDebugElementAdapterFactory propertiesFactory = new PHPDebugElementAdapterFactory(); manager.registerAdapters(propertiesFactory, IVariable.class); manager.registerAdapters(propertiesFactory, IWatchExpression.class); manager.registerAdapters(propertiesFactory, PHPLaunch.class); manager.registerAdapters(propertiesFactory, PHPMultiDebugTarget.class); manager.registerAdapters(propertiesFactory, DBGpMultiSessionTarget.class); // In case the list had some factories, make sure our factory is the // first in the list. if (list != null && list.size() > 1) { list.remove(propertiesFactory); list.add(0, propertiesFactory); } // Perform a clean on the secured storage, which might contain SSH // tunnels passwords that no longer being used by any of the defined // launch configurations. cleanSecuredStorage(); EvaluationContextManager.startup(); } /** * This method is called when the plug-in is stopped */ public void stop(BundleContext context) throws Exception { super.stop(context); if (showViewListener != null) { DebugPlugin.getDefault().removeDebugEventListener(showViewListener); } // Uninstall the debug event listener. if (finishDebugLaunchListener != null) { DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(finishDebugLaunchListener); } // Uninstall the debug event listener. if (firstSelectionDebugLaunchListener != null) { DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(firstSelectionDebugLaunchListener); } plugin = null; } /** * Returns the shared instance. */ public static PHPDebugUIPlugin getDefault() { return plugin; } /** * Returns the PHP debug ID. */ public static String getID() { return ID; } /** * Returns an image descriptor for the image file at the given plug-in * relative path. * * @param path * the path * @return the image descriptor */ public static ImageDescriptor getImageDescriptor(String path) { return AbstractUIPlugin.imageDescriptorFromPlugin("org.eclipse.php.debug.ui", path); //$NON-NLS-1$ } /** * Returns the image descriptor registry used for this plugin. */ public static ImageDescriptorRegistry getImageDescriptorRegistry() { if (getDefault().fImageDescriptorRegistry == null) { PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { @Override public void run() { getDefault().fImageDescriptorRegistry = new ImageDescriptorRegistry(); } }); } return getDefault().fImageDescriptorRegistry; } /** * Returns the active workbench window * * @return the active workbench window */ public static IWorkbenchWindow getActiveWorkbenchWindow() { return getDefault().getWorkbench().getActiveWorkbenchWindow(); } public static IWorkbenchPage getActivePage() { IWorkbenchWindow w = getActiveWorkbenchWindow(); if (w != null) { return w.getActivePage(); } return null; } /** * Returns the active workbench shell or <code>null</code> if none * * @return the active workbench shell or <code>null</code> if none */ public static Shell getActiveWorkbenchShell() { IWorkbenchWindow window = getActiveWorkbenchWindow(); if (window != null) { return window.getShell(); } return null; } public static Display getStandardDisplay() { Display display; display = Display.getCurrent(); if (display == null) display = Display.getDefault(); return display; } public static void showView(String viewID) { IWorkbenchWindow window = getDefault().getWorkbench().getActiveWorkbenchWindow(); if (window != null) { IWorkbenchPage page = window.getActivePage(); if (page != null) { IPerspectiveDescriptor descriptor = page.getPerspective(); if (descriptor.getId().indexOf("php") != -1) { //$NON-NLS-1$ IViewPart part = page.findView(viewID); if (part == null) { try { page.showView(viewID); } catch (PartInitException e) { ErrorDialog.openError(window.getShell(), PHPDebugUIMessages.ShowView_errorTitle, e.getMessage(), e.getStatus()); } } else { // page.bringToTop(part); } } } } } public static void log(IStatus status) { getDefault().getLog().log(status); } public static void log(Throwable e) { log(new Status(IStatus.ERROR, ID, INTERNAL_ERROR, "PHP ui plugin internal error", e)); //$NON-NLS-1$ } public static void logErrorMessage(String message) { log(new Status(IStatus.ERROR, ID, INTERNAL_ERROR, message, null)); } public static IProject getProject(String projectName) { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); IProject[] projects = workspaceRoot.getProjects(); IProject project = null; for (IProject element : projects) { if (element.getName().equals(projectName)) { project = element; break; } } return project; } /* * Clean the secure storage from any SSH Tunnel definitions that are no * longer used by any of the launch configuration definitions. */ private void cleanSecuredStorage() { Job cleanStorageJob = new Job(PHPDebugUIMessages.PHPDebugUIPlugin_2) { protected IStatus run(IProgressMonitor monitor) { try { // First, collect all the tunnel definitions in the launch // configurations. HashMap<String, List<String>> hostToUsers = new HashMap<String, List<String>>(); ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType configurationType = launchManager .getLaunchConfigurationType(IPHPDebugConstants.PHPServerLaunchType); ILaunchConfiguration[] configs = launchManager.getLaunchConfigurations(configurationType); for (ILaunchConfiguration configuration : configs) { addTunnelConfiguration(configuration, hostToUsers); } // Then, check with the secure-storage node and remove // anything that should not be there. ISecurePreferences root = SecurePreferencesFactory.getDefault(); ISecurePreferences node = root.node(IPHPDebugConstants.SSH_TUNNEL_SECURE_PREF_NODE); String[] listedHosts = node.childrenNames(); // For each host that we have in the secured storage, check // that it's existing in the launch configurations and that // it contains // only the user names that are defined. // In any other case, remove the item from the storage (a // host node, or a user node). for (String host : listedHosts) { if (!hostToUsers.containsKey(host)) { ISecurePreferences hostNode = node.node(host); hostNode.removeNode(); } else { if (node.nodeExists(host)) { ISecurePreferences hostNode = node.node(host); // holds the user names field in the secure // preference String[] usersKeys = hostNode.keys(); List<String> usersList = hostToUsers.get(host); for (String userNode : usersKeys) { if (!usersList.contains(userNode)) { hostNode.remove(userNode); } } } } } } catch (Exception e) { Logger.logException( "Error while cleaning up the php debug tunnels credentials from the secured storage", //$NON-NLS-1$ e); } return Status.OK_STATUS; } }; cleanStorageJob.setSystem(true); cleanStorageJob.setPriority(Job.LONG); // No rush. We can schedule it to run a few seconds after the loading. cleanStorageJob.schedule(30000L); } /* * Potentially, add a tunnel configuration that exists in the given launch * configuration. If non exists, nothing is added. */ private void addTunnelConfiguration(ILaunchConfiguration configuration, HashMap<String, List<String>> hostToUsers) throws CoreException { boolean isUsingTunnel = configuration.getAttribute(IPHPDebugConstants.USE_SSH_TUNNEL, false); if (isUsingTunnel) { String userName = configuration.getAttribute(IPHPDebugConstants.SSH_TUNNEL_USER_NAME, ""); //$NON-NLS-1$ if (userName.length() > 0) { String debugHost = PHPLaunchUtilities.getDebugHost(configuration); List<String> users = hostToUsers.get(debugHost); if (users == null) { users = new ArrayList<String>(3); hostToUsers.put(debugHost, users); } users.add(userName); } } } private static class ShowViewListener implements IDebugEventSetListener { public void handleDebugEvents(DebugEvent[] events) { if (events != null) { int size = events.length; for (int i = 0; i < size; i++) { DebugEvent event = events[i]; if (event.getKind() == DebugEvent.CREATE) { Object obj = events[i].getSource(); if (!(obj instanceof IPHPDebugTarget)) continue; if (PHPDebugPlugin.getOpenDebugViewsOption()) { Job job = new org.eclipse.ui.progress.UIJob(PHPDebugUIMessages.PHPDebugUIPlugin_0) { public IStatus runInUIThread(IProgressMonitor monitor) { showView(DebugBrowserView.ID_PHPBrowserOutput); showView(DebugOutputView.ID_PHPDebugOutput); showView(IConsoleConstants.ID_CONSOLE_VIEW); return Status.OK_STATUS; } }; job.schedule(); } } if (event.getKind() == DebugEvent.MODEL_SPECIFIC) { Object obj = events[i].getSource(); if (!(obj instanceof IPHPDebugTarget)) continue; final Object data = events[i].getData(); if (data instanceof IStatus) { Job job = new org.eclipse.ui.progress.UIJob(PHPDebugUIMessages.PHPDebugUIPlugin_0) { public IStatus runInUIThread(IProgressMonitor monitor) { IStatus status = (IStatus) data; Shell shell = getActiveWorkbenchShell(); ErrorDialog.openError(shell, null, null, status); return Status.OK_STATUS; } }; job.schedule(); } } } } } }; /* * A class that is responsible of asking the user to return to the PHP * perspective when all the debug sessions where terminated. */ private static class TerminateDebugLaunchListener implements ILaunchesListener2 { /** * Handle only the termination events. */ public void launchesTerminated(ILaunch[] launches) { boolean isPHPLaunch = false; for (ILaunch launch : launches) { if (launch instanceof PHPLaunch) { isPHPLaunch = true; break; } } if (!isPHPLaunch) { return; } // In case we have an active debug launches, we can terminate the // event handling because there is no need to switch perspective. Display.getDefault().asyncExec(new Runnable() { public void run() { if (!PHPLaunchUtilities.hasPHPDebugLaunch()) { PHPLaunchUtilities.switchToPHPPerspective(); } } }); } public void launchesAdded(ILaunch[] launches) { } public void launchesChanged(ILaunch[] launches) { } public void launchesRemoved(ILaunch[] launches) { } } /* * A class that is responsible of selecting the new launch in the * LaunchView. */ private static class FirstSelectionDebugLaunchListener implements ILaunchesListener2 { public void launchesTerminated(ILaunch[] launches) { } /** * handle only add new launch event */ public void launchesAdded(ILaunch[] launches) { if (launches != null && launches.length > 0) { final ILaunch[] currentLaunches = launches; Display.getDefault().asyncExec(new Runnable() { public void run() { // get the LaunchView IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (activeWorkbenchWindow == null) { return; } IWorkbenchPage page = activeWorkbenchWindow.getActivePage(); if (page == null) return; LaunchView view = (LaunchView) page.findView(IDebugUIConstants.ID_DEBUG_VIEW); if (view == null) return; try { // build the tree path // LaunchManager->Launch->DebugTarget->PHPThread->PHPStackFrame IDebugTarget target = currentLaunches[0].getDebugTarget(); if (target == null) return; if (target.getThreads() == null || target.getThreads().length == 0) return; IThread thread = target.getThreads()[0]; IStackFrame frame = thread.getTopStackFrame(); if (frame == null) return; Object[] segments = new Object[5]; segments[0] = DebugPlugin.getDefault().getLaunchManager(); segments[1] = currentLaunches[0]; segments[2] = target; segments[3] = thread; segments[4] = frame; TreePath treePath = new TreePath(segments); // set the current launch as the LaunchViewer // selection. ((InternalTreeModelViewer) view.getViewer()).setSelection(new TreeSelection(treePath), true, true); } catch (DebugException e) { } } }); } } public void launchesChanged(ILaunch[] launches) { } public void launchesRemoved(ILaunch[] launches) { } } /** * Returns a shared utility PHP debug model presentation. Clients should not * dispose the presentation. * * @return a PHP debug model presentation */ public IDebugModelPresentation getModelPresentation() { if (fUtilPresentation == null) { fUtilPresentation = DebugUITools.newDebugModelPresentation(IPHPDebugConstants.ID_PHP_DEBUG_CORE); } return fUtilPresentation; } }