| /******************************************************************************* |
| * Copyright (c) 2018 Cedric Chabanois 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: |
| * Cedric Chabanois (cchabanois@gmail.com) - initial implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.debug.tests.launching; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Optional; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| import org.eclipse.debug.core.model.IProcess; |
| import org.eclipse.jdt.core.IClasspathAttribute; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.debug.core.IJavaThread; |
| import org.eclipse.jdt.debug.testplugin.JavaProjectHelper; |
| import org.eclipse.jdt.debug.tests.AbstractDebugTest; |
| import org.eclipse.jdt.internal.launching.LaunchingPlugin; |
| import org.eclipse.jdt.internal.ui.wizards.buildpaths.BuildPathSupport; |
| import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; |
| import org.eclipse.jdt.launching.JavaRuntime; |
| import org.eclipse.jdt.launching.environments.IExecutionEnvironment; |
| |
| /** |
| * Test long module-path. OSs have limits in term of command line length or argument length. We use an argument file when module-path is too long. |
| * |
| */ |
| public class LongModulePathTests extends AbstractDebugTest { |
| private static final IPath CLASSPATH_PROJECT_CONTENT_PATH = new Path("testresources/classpathModuleProject"); |
| private static final String MAIN_TYPE_NAME = "test.classpath.Main"; |
| private IJavaProject javaProject; |
| private IJavaThread thread; |
| private ILaunchConfiguration launchConfiguration; |
| |
| public LongModulePathTests(String name) { |
| super(name); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| try { |
| if (thread != null) { |
| terminateAndRemove(thread); |
| } |
| if (javaProject != null) { |
| javaProject.getProject().delete(true, true, null); |
| } |
| if (launchConfiguration != null) { |
| launchConfiguration.delete(); |
| } |
| } catch (CoreException ce) { |
| // ignore |
| } finally { |
| super.tearDown(); |
| } |
| } |
| |
| /* |
| * When JVM > 9, an argument file for the modulepath is created when modulepath is too long |
| */ |
| public void testVeryLongModulepathWithArgumentFile() throws Exception { |
| // Given |
| javaProject = createJavaProjectClone("testVeryLongModulePath", CLASSPATH_PROJECT_CONTENT_PATH.toString(), JavaProjectHelper.JAVA_SE_9_EE_NAME, true); |
| useComplianceFromExecutionEnvironment(javaProject); |
| useModuleForJREContainer(javaProject); |
| launchConfiguration = createLaunchConfigurationStopInMain(javaProject, MAIN_TYPE_NAME); |
| int minModulePathLength = 300000; |
| setLongModulepath(javaProject, minModulePathLength); |
| waitForBuild(); |
| |
| // When |
| thread = launchAndSuspend(launchConfiguration); |
| |
| // Then |
| File tempFile = getTempFile(thread.getLaunch()).orElseThrow(() -> new RuntimeException("No temp file")); |
| assertTrue(tempFile.exists()); |
| assertTrue(tempFile.getName().endsWith(".txt")); |
| assertTrue(doEval(thread, "System.getProperty(\"jdk.module.path\")").getValueString().length() >= minModulePathLength); |
| |
| // When |
| resumeAndExit(thread); |
| |
| // Then |
| if (!Platform.getOS().equals(Platform.OS_WIN32)) { |
| // On windows, temp file deletion may fail |
| assertFalse(tempFile.exists()); |
| } |
| } |
| |
| private ILaunchConfiguration createLaunchConfigurationStopInMain(IJavaProject javaProject, String mainTypeName) throws Exception, CoreException { |
| ILaunchConfiguration launchConfiguration; |
| launchConfiguration = createLaunchConfiguration(javaProject, mainTypeName); |
| ILaunchConfigurationWorkingCopy wc = launchConfiguration.getWorkingCopy(); |
| wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_STOP_IN_MAIN, true); |
| launchConfiguration = wc.doSave(); |
| return launchConfiguration; |
| } |
| |
| private void setLongModulepath(IJavaProject javaProject, int minModulePathLength) throws Exception { |
| StringBuilder sb = new StringBuilder(); |
| List<IClasspathEntry> classpathEntries = new ArrayList<>(); |
| int i = 0; |
| while (sb.length() < minModulePathLength) { |
| String jarName = "library" + i + ".jar"; |
| IPath targetPath = javaProject.getPath().append("lib/" + jarName); |
| javaProject.getProject().getFile("lib/classpath.jar").copy(targetPath, IResource.FORCE, new NullProgressMonitor()); |
| IClasspathAttribute moduleClasspathAttribute = JavaCore.newClasspathAttribute(IClasspathAttribute.MODULE, "true"); |
| classpathEntries.add(JavaCore.newLibraryEntry(targetPath, null, null, null, new IClasspathAttribute[] { |
| moduleClasspathAttribute }, false)); |
| if (i != 0) { |
| sb.append(File.pathSeparator); |
| } |
| sb.append(javaProject.getProject().getFile("lib/" + jarName).getLocation().toString()); |
| i++; |
| } |
| IClasspathAttribute moduleClasspathAttribute = JavaCore.newClasspathAttribute(IClasspathAttribute.MODULE, "true"); |
| classpathEntries.add(JavaCore.newLibraryEntry(javaProject.getPath().append("lib/classpath.jar"), null, null, null, new IClasspathAttribute[] { |
| moduleClasspathAttribute }, false)); |
| sb.append(File.pathSeparator); |
| sb.append(javaProject.getProject().getFile("lib/classpath.jar").getLocation().toString()); |
| classpathEntries.addAll(Arrays.asList(javaProject.getRawClasspath())); |
| javaProject.setRawClasspath(classpathEntries.toArray(new IClasspathEntry[classpathEntries.size()]), null); |
| } |
| |
| private void useComplianceFromExecutionEnvironment(IJavaProject javaProject) throws JavaModelException { |
| IExecutionEnvironment executionEnvironment = getExecutionEnvironment(javaProject); |
| Map<String, String> eeOptions = BuildPathSupport.getEEOptions(executionEnvironment); |
| eeOptions.forEach((optionName, optionValue) -> javaProject.setOption(optionName, optionValue)); |
| } |
| |
| private IExecutionEnvironment getExecutionEnvironment(IJavaProject javaProject) throws JavaModelException { |
| IClasspathEntry[] entries = javaProject.getRawClasspath(); |
| for (int i = 0; i < entries.length; i++) { |
| IClasspathEntry entry = entries[i]; |
| if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { |
| String eeId = JavaRuntime.getExecutionEnvironmentId(entry.getPath()); |
| if (eeId != null) { |
| return JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(eeId); |
| } |
| } |
| } |
| return null; |
| } |
| |
| private void useModuleForJREContainer(IJavaProject javaProject) throws JavaModelException { |
| IClasspathEntry[] rawClasspath = javaProject.getRawClasspath(); |
| for (int i = 0; i < rawClasspath.length; i++) { |
| IClasspathEntry classpathEntry = rawClasspath[i]; |
| if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { |
| IClasspathAttribute moduleClasspathAttribute = JavaCore.newClasspathAttribute(IClasspathAttribute.MODULE, "true"); |
| classpathEntry = JavaCore.newContainerEntry(classpathEntry.getPath(), classpathEntry.getAccessRules(), new IClasspathAttribute[] { |
| moduleClasspathAttribute }, classpathEntry.isExported()); |
| rawClasspath[i] = classpathEntry; |
| } |
| } |
| javaProject.setRawClasspath(rawClasspath, null); |
| } |
| |
| private Optional<File> getTempFile(ILaunch launch) { |
| IProcess process = launch.getProcesses()[0]; |
| String tempFile = process.getAttribute(LaunchingPlugin.ATTR_LAUNCH_TEMP_FILES); |
| if (tempFile == null) { |
| return Optional.empty(); |
| } |
| return Optional.of(new File(tempFile)); |
| } |
| |
| } |