// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package net.sourceforge.eclipsejetty.launch.shortcut; import static net.sourceforge.eclipsejetty.launch.util.JettyLaunchUI.*; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import net.sourceforge.eclipsejetty.JettyPlugin; import net.sourceforge.eclipsejetty.JettyPluginUtils; import net.sourceforge.eclipsejetty.Messages; import net.sourceforge.eclipsejetty.launch.util.JettyLaunchConfigurationAdapter; import net.sourceforge.eclipsejetty.launch.util.JettyLaunchUI; import net.sourceforge.eclipsejetty.launch.util.JettyLaunchUtils; import net.sourceforge.eclipsejetty.launch.util.LaunchConfigurationLabelProvider; import net.sourceforge.eclipsejetty.util.Result; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.ILaunchShortcut2; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import org.eclipse.ui.part.FileEditorInput; /** * The "Run As..." and "Debug As..." shortcut. Basically, works on any single resource. Tries to locate the web * application folder within the project and creates a launch configuration. * * @author Manfred Hantschel */ public class JettyLaunchShortcut implements ILaunchShortcut2 { private static final String LAUNCH_CONFIGURATION_TYPE = "net.sourceforge.eclipsejetty.launchConfigurationType"; //$NON-NLS-1$ /** * {@inheritDoc} * * @see org.eclipse.debug.ui.ILaunchShortcut#launch(org.eclipse.jface.viewers.ISelection, java.lang.String) */ public void launch(ISelection selection, String mode) { IResource resource = getResource(selection); if (resource == null) { return; } launch(resource, mode); } /** * {@inheritDoc} * * @see org.eclipse.debug.ui.ILaunchShortcut#launch(org.eclipse.ui.IEditorPart, java.lang.String) */ public void launch(IEditorPart editorPart, String mode) { IResource resource = getResource(editorPart); if (resource == null) { return; } launch(resource, mode); } /** * Launches the Jetty by using an existing launch configuration or by creating a new one. Prefers to find the * WEB-INF/web.xml at or beneath the specified resource. If not WEB-INF/web.xml was not found there, it tries to * locate it within the project. If still no resource was found, it asks the user to specify one. * * @param resource any resource of a project or the project itself * @param mode the launch mode (RUN or DEBUG) */ protected void launch(IResource resource, String mode) { launch(resource.getProject(), resource, mode); } /** * Launches the Jetty by using an existing launch configuration or by creating a new one. Prefers to find the * WEB-INF/web.xml at or beneath the specified resource. If not WEB-INF/web.xml was not found there, it tries to * locate it within the project. If still no resource was found, it asks the user to specify one. * * @param project the project * @param resource the resource * @param mode the launch mode (RUN or DEBUG) */ protected void launch(final IProject project, IResource resource, String mode) { ILaunchConfiguration[] existingLaunchConfigurations = null; Set<IResource> webXMLResources = new LinkedHashSet<IResource>(findWebXMLResources(resource)); if (webXMLResources.size() > 0) { // found web.xml files at the resource, search for existing launch configurations existingLaunchConfigurations = getExistingLaunchConfigurations(project, toWebAppPaths(project, webXMLResources)); } if ((existingLaunchConfigurations == null) || (existingLaunchConfigurations.length == 0)) { // did not find existing launch configurations, search for web.xml in the whole project webXMLResources.addAll(findWebXMLResources(project)); if (webXMLResources.size() > 0) { // found web.xml files in the project, search for existing launch configurations existingLaunchConfigurations = getExistingLaunchConfigurations(project, toWebAppPaths(project, webXMLResources)); } } ILaunchConfiguration launchConfiguration = null; if ((existingLaunchConfigurations == null) || (existingLaunchConfigurations.length == 0)) { launchConfiguration = createLaunchConfiguration(project, new ArrayList<IResource>(webXMLResources)); } else { launchConfiguration = selectLaunchConfiguration(existingLaunchConfigurations); } if (launchConfiguration == null) { return; } DebugUITools.launch(launchConfiguration, mode); } /** * Creates a new launch configuration with a web app path selected from one of the specified web.xml resources. If * not web.xml resource was specified, it asks the user to specify one. If one web.xml resource was provided, it * uses this web.xml resource. If multiple web.xml resources were specified, it asks the user to select one. * * @param project the project * @param webXMLResources the web.xml resource * @return the launch configuration, null if none was created due to user interruption */ protected ILaunchConfiguration createLaunchConfiguration(final IProject project, List<IResource> webXMLResources) { File webAppPath = null; if (webXMLResources.size() == 0) { Display.getCurrent().syncExec(new Runnable() { public void run() { MessageDialog.openError(Display.getCurrent().getActiveShell(), Messages.shortcut_webAppDirNotFoundTitle, String.format(Messages.shortcut_webAppDirNotFoundMessage, project.getName())); } }); String path = chooseWorkspaceDirectory(Display.getCurrent().getActiveShell(), JettyPluginUtils.getProject(project.getName()), Messages.shortcut_webAppSelectTitle, Messages.shortcut_webAppSelectMessage, null); if (path == null) { return null; } webAppPath = JettyPluginUtils.resolveFolder(project, path); } else if (webXMLResources.size() == 1) { webAppPath = JettyPluginUtils.resolveFolder(project, webXMLResources.get(0).getFullPath().removeLastSegments(2) .toString()); } else { try { String webAppDir = JettyLaunchUI.chooseWebAppDir(Display.getCurrent().getActiveShell(), project, JettyLaunchUtils.toWebappDirs(webXMLResources), project.getFullPath().toString()); if (webAppDir == null) { return null; } webAppPath = JettyPluginUtils.resolveFolder(project, webAppDir); } catch (CoreException e) { JettyPlugin.error("Failed to choose Web App directory", e); return null; } } return createLaunchConfiguration(project, webAppPath); } /** * Select one launch configuration. If zero launch configuration are specified, the method return null. If one * launch configuration is specified, it returns this one. If multiple launch configurations are specified, it asks * the user to select one. * * @param existingLaunchConfigurations the launch configurations * @return a selected launch configuration, or null */ protected ILaunchConfiguration selectLaunchConfiguration(final ILaunchConfiguration[] existingLaunchConfigurations) { if ((existingLaunchConfigurations == null) || (existingLaunchConfigurations.length == 0)) { return null; } if (existingLaunchConfigurations.length == 1) { return existingLaunchConfigurations[0]; } final Result<ILaunchConfiguration> result = new Result<ILaunchConfiguration>(); Display.getCurrent().syncExec(new Runnable() { public void run() { ElementListSelectionDialog dialog = new ElementListSelectionDialog(Display.getCurrent().getActiveShell(), new LaunchConfigurationLabelProvider()); dialog.setElements(existingLaunchConfigurations); dialog.setTitle("Choose Launch Configuration"); dialog.setMessage("Multiple launch configuration apply to your selection.\nPlease choose one:"); dialog.setMultipleSelection(false); if (dialog.open() != Window.OK) { return; } result.setResult((ILaunchConfiguration) dialog.getResult()[0]); } }); try { return result.getResult(); } catch (CoreException e) { JettyPlugin.error("Failed to retrieve result", e); } return null; } /** * {@inheritDoc} * * @see org.eclipse.debug.ui.ILaunchShortcut2#getLaunchConfigurations(org.eclipse.jface.viewers.ISelection) */ public ILaunchConfiguration[] getLaunchConfigurations(ISelection selection) { IResource resource = getResource(selection); if (resource == null) { return null; } return getLaunchConfigurations(resource); } /** * {@inheritDoc} * * @see org.eclipse.debug.ui.ILaunchShortcut2#getLaunchConfigurations(org.eclipse.ui.IEditorPart) */ public ILaunchConfiguration[] getLaunchConfigurations(IEditorPart editorpart) { IResource resource = getResource(editorpart); if (resource == null) { return null; } return getLaunchConfigurations(resource); } /** * {@inheritDoc} * * @see org.eclipse.debug.ui.ILaunchShortcut2#getLaunchableResource(org.eclipse.jface.viewers.ISelection) */ public IResource getLaunchableResource(ISelection selection) { return getResource(selection); } /** * {@inheritDoc} * * @see org.eclipse.debug.ui.ILaunchShortcut2#getLaunchableResource(org.eclipse.ui.IEditorPart) */ public IResource getLaunchableResource(IEditorPart editorPart) { return getResource(editorPart); } /** * Tries to grab the resource of the selection. * * @param selection the selection * @return the resource, null if failed to grab */ protected IResource getResource(ISelection selection) { if (selection instanceof TreeSelection) { Object element = ((TreeSelection) selection).getFirstElement(); if (element instanceof IResource) { return (IResource) element; } if (element instanceof IJavaElement) { return ((IJavaElement) element).getResource(); } JettyPlugin.warning(String.format(Messages.shortcut_unsupportedLaunchSelectionElement, element.getClass())); return null; } JettyPlugin.warning(String.format(Messages.shortcut_unsupportedLaunchSelection, selection.getClass())); return null; } /** * Tries to grab the resource from the editor. * * @param editorPart the editor part * @return the resource, null if failed to grab */ protected IResource getResource(IEditorPart editorPart) { FileEditorInput fileEditorInput = (FileEditorInput) editorPart.getEditorInput().getAdapter(FileEditorInput.class); if (fileEditorInput == null) { JettyPlugin.warning(Messages.shortcut_invalidEditorInput); return null; } if (fileEditorInput.getFile() == null) { JettyPlugin.warning(Messages.shortcut_invalidEditorInputFile); return null; } return fileEditorInput.getFile(); } /** * Searches for existing launch configurations. Prefers to find the WEB-INF/web.xml at or beneath the specified * resource. If not WEB-INF/web.xml was not found there, it tries to locate it within the project. * * @param resource any resource of a project or the project itself * @return an array of existing launch configuration, empty if none was found, null if unable to create one (e.g. no * WEB-INF/web.xml was found). */ protected ILaunchConfiguration[] getLaunchConfigurations(IResource resource) { return getLaunchConfigurations(resource.getProject(), resource); } /** * Searches for existing launch configurations. Prefers to find the WEB-INF/web.xml at or beneath the specified * resource. If not WEB-INF/web.xml was not found there, it tries to locate it within the project. * * @param resource the WEB-INF/web.xml file. If null, tries to locate the file first beneath the resource, then * within the project. * @return an array of existing launch configuration, empty if none was found, null if unable to create one (e.g. no * WEB-INF/web.xml was found). */ protected ILaunchConfiguration[] getLaunchConfigurations(final IProject project, IResource resource) { // TODO stricktly spoken, it should first search the specified resource, as // those files are preferred by the launch method, and then search the project // but the following lines only search the project which may result it more // files than ususal List<IResource> webXMLResources = findWebXMLResources(project); if (webXMLResources.size() <= 0) { return null; } List<File> webAppPaths = toWebAppPaths(project, webXMLResources); if (webAppPaths.size() <= 0) { return null; } return getExistingLaunchConfigurations(project, webAppPaths); } /** * Returns all existing launch configurations with the specified project and one of the the specified web * application paths. * * @param project the project, must no be null * @param webAppPaths the web application paths, must no be null * @return all existing launch configurations, never null */ protected ILaunchConfiguration[] getExistingLaunchConfigurations(IProject project, List<File> webAppPaths) { List<ILaunchConfiguration> results = new ArrayList<ILaunchConfiguration>(); String projectName = project.getName(); try { ILaunchConfiguration[] configurations = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations(); for (ILaunchConfiguration configuration : configurations) { ILaunchConfigurationType type = configuration.getType(); JettyLaunchConfigurationAdapter adapter = JettyLaunchConfigurationAdapter.getInstance(configuration); if (LAUNCH_CONFIGURATION_TYPE.equals(type.getIdentifier())) { if (!projectName.equals(adapter.getProjectName())) { continue; } File currentWebAppPath = adapter.getWebAppPath(); if (currentWebAppPath == null) { continue; } currentWebAppPath = currentWebAppPath.getAbsoluteFile(); if (!webAppPaths.contains(currentWebAppPath)) { continue; } results.add(configuration); } } } catch (CoreException e) { // ignore } return results.toArray(new ILaunchConfiguration[results.size()]); } /** * Creates a new launch configuration. * * @param project the project, must not be null * @param webAppPath the web application path, must not be null * @return the launch configuration, null if there was an error */ protected ILaunchConfigurationWorkingCopy createLaunchConfiguration(IProject project, File webAppPath) { try { ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType launchConfigurationType = launchManager.getLaunchConfigurationType(LAUNCH_CONFIGURATION_TYPE); String name = JettyLaunchUtils.generateLaunchConfigurationName(project); ILaunchConfigurationWorkingCopy configuration = launchConfigurationType.newInstance(null, name); JettyLaunchConfigurationAdapter adapter = JettyLaunchConfigurationAdapter.getInstance(configuration); adapter.initialize(project, webAppPath); configuration.setMappedResources(new IResource[]{project}); configuration.doSave(); return configuration; } catch (CoreException e) { JettyPlugin.error(Messages.shortcut_createFailed, e); } return null; } /** * Converts the list of webXMLResources to webAppPaths by removing the last two segments * * @param project the project * @param webXMLResources the resource * @return the files */ protected List<File> toWebAppPaths(final IProject project, Collection<IResource> webXMLResources) { List<File> webAppPaths = new ArrayList<File>(); for (IResource webXMLResource : webXMLResources) { IPath webAppResource = webXMLResource.getFullPath().removeLastSegments(2); File webAppPath = JettyPluginUtils.resolveFolder(project, webAppResource.toString()); if (webAppPath != null) { webAppPaths.add(webAppPath); } } return webAppPaths; } /** * Tries to locate the web.xml file within the specified resource. Searches sub-folders if the resource points to a * folder. Returns a list of resources. * * @param resource the resource, may be null * @return a list of web.xml resources, empty list if none was found */ protected List<IResource> findWebXMLResources(IResource resource) { try { return JettyLaunchUtils.findWebXMLs(resource, Integer.MAX_VALUE); } catch (CoreException e) { // ignore } return Collections.<IResource> emptyList(); } /** * Tries to locate all the web.xml files within the specified resource and the project. * * @param resource the resource, may be null * @param project the project, may be null * @return all the web.xml files as resource, null if not found */ protected List<IResource> findWebXMLResources(IProject project) { try { return JettyLaunchUtils.findWebXMLs(project, Integer.MAX_VALUE); } catch (CoreException e) { // ignore } return null; } }