1 /******************************************************************************* 2 * Copyright (c) 2000, 2020 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching 14 * external annotations to a JRE container 15 *******************************************************************************/ 16 package org.eclipse.jdt.launching; 17 18 19 import java.io.BufferedInputStream; 20 import java.io.ByteArrayInputStream; 21 import java.io.File; 22 import java.io.FileInputStream; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.io.StringReader; 26 import java.net.MalformedURLException; 27 import java.net.URL; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collection; 31 import java.util.Collections; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Hashtable; 35 import java.util.Iterator; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.Set; 40 import java.util.stream.Collectors; 41 42 import javax.xml.parsers.DocumentBuilder; 43 44 import org.eclipse.core.resources.IProject; 45 import org.eclipse.core.resources.IResource; 46 import org.eclipse.core.resources.IWorkspaceRoot; 47 import org.eclipse.core.resources.ResourcesPlugin; 48 import org.eclipse.core.runtime.CoreException; 49 import org.eclipse.core.runtime.IConfigurationElement; 50 import org.eclipse.core.runtime.IExtensionPoint; 51 import org.eclipse.core.runtime.IPath; 52 import org.eclipse.core.runtime.IProgressMonitor; 53 import org.eclipse.core.runtime.IStatus; 54 import org.eclipse.core.runtime.ListenerList; 55 import org.eclipse.core.runtime.MultiStatus; 56 import org.eclipse.core.runtime.Path; 57 import org.eclipse.core.runtime.Platform; 58 import org.eclipse.core.runtime.Preferences; 59 import org.eclipse.core.runtime.Status; 60 import org.eclipse.core.runtime.preferences.BundleDefaultsScope; 61 import org.eclipse.core.runtime.preferences.IEclipsePreferences; 62 import org.eclipse.core.runtime.preferences.InstanceScope; 63 import org.eclipse.core.variables.IStringVariableManager; 64 import org.eclipse.core.variables.VariablesPlugin; 65 import org.eclipse.debug.core.ILaunchConfiguration; 66 import org.eclipse.debug.core.sourcelookup.ISourceContainer; 67 import org.eclipse.jdt.core.IAccessRule; 68 import org.eclipse.jdt.core.IClasspathAttribute; 69 import org.eclipse.jdt.core.IClasspathContainer; 70 import org.eclipse.jdt.core.IClasspathEntry; 71 import org.eclipse.jdt.core.IJavaModel; 72 import org.eclipse.jdt.core.IJavaProject; 73 import org.eclipse.jdt.core.IModuleDescription; 74 import org.eclipse.jdt.core.IPackageFragmentRoot; 75 import org.eclipse.jdt.core.JavaCore; 76 import org.eclipse.jdt.core.JavaModelException; 77 import org.eclipse.jdt.internal.core.JavaProject; 78 import org.eclipse.jdt.internal.launching.CompositeId; 79 import org.eclipse.jdt.internal.launching.DefaultEntryResolver; 80 import org.eclipse.jdt.internal.launching.DefaultProjectClasspathEntry; 81 import org.eclipse.jdt.internal.launching.EEVMInstall; 82 import org.eclipse.jdt.internal.launching.EEVMType; 83 import org.eclipse.jdt.internal.launching.JREContainerInitializer; 84 import org.eclipse.jdt.internal.launching.JavaSourceLookupUtil; 85 import org.eclipse.jdt.internal.launching.LaunchingMessages; 86 import org.eclipse.jdt.internal.launching.LaunchingPlugin; 87 import org.eclipse.jdt.internal.launching.RuntimeClasspathEntry; 88 import org.eclipse.jdt.internal.launching.RuntimeClasspathEntryResolver; 89 import org.eclipse.jdt.internal.launching.RuntimeClasspathProvider; 90 import org.eclipse.jdt.internal.launching.SocketAttachConnector; 91 import org.eclipse.jdt.internal.launching.StandardVMType; 92 import org.eclipse.jdt.internal.launching.VMDefinitionsContainer; 93 import org.eclipse.jdt.internal.launching.VMListener; 94 import org.eclipse.jdt.internal.launching.VariableClasspathEntry; 95 import org.eclipse.jdt.internal.launching.environments.EnvironmentsManager; 96 import org.eclipse.jdt.launching.environments.ExecutionEnvironmentDescription; 97 import org.eclipse.jdt.launching.environments.IExecutionEnvironment; 98 import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager; 99 import org.eclipse.osgi.util.NLS; 100 import org.osgi.service.prefs.BackingStoreException; 101 import org.w3c.dom.Element; 102 import org.w3c.dom.Node; 103 import org.w3c.dom.NodeList; 104 import org.xml.sax.InputSource; 105 import org.xml.sax.SAXException; 106 107 /** 108 * The central access point for launching support. This class manages 109 * the registered VM types contributed through the 110 * <code>"org.eclipse.jdt.launching.vmType"</code> extension point. 111 * As well, this class provides VM install change notification, 112 * and computes class paths and source lookup paths for launch 113 * configurations. 114 * <p> 115 * This class provides static methods only. 116 * </p> 117 * @noinstantiate This class is not intended to be instantiated by clients. 118 */ 119 public final class JavaRuntime { 120 121 /** 122 * Classpath variable name used for the default JRE's library 123 * (value <code>"JRE_LIB"</code>). 124 */ 125 public static final String JRELIB_VARIABLE= "JRE_LIB"; //$NON-NLS-1$ 126 127 /** 128 * Classpath variable name used for the default JRE's library source 129 * (value <code>"JRE_SRC"</code>). 130 */ 131 public static final String JRESRC_VARIABLE= "JRE_SRC"; //$NON-NLS-1$ 132 133 /** 134 * Classpath variable name used for the default JRE's library source root 135 * (value <code>"JRE_SRCROOT"</code>). 136 */ 137 public static final String JRESRCROOT_VARIABLE= "JRE_SRCROOT"; //$NON-NLS-1$ 138 139 /** 140 * Simple identifier constant (value <code>"runtimeClasspathEntryResolvers"</code>) for the 141 * runtime classpath entry resolvers extension point. 142 * 143 * @since 2.0 144 */ 145 public static final String EXTENSION_POINT_RUNTIME_CLASSPATH_ENTRY_RESOLVERS= "runtimeClasspathEntryResolvers"; //$NON-NLS-1$ 146 147 /** 148 * Simple identifier constant (value <code>"classpathProviders"</code>) for the 149 * runtime classpath providers extension point. 150 * 151 * @since 2.0 152 */ 153 public static final String EXTENSION_POINT_RUNTIME_CLASSPATH_PROVIDERS= "classpathProviders"; //$NON-NLS-1$ 154 155 /** 156 * Simple identifier constant (value <code>"executionEnvironments"</code>) for the 157 * execution environments extension point. 158 * 159 * @since 3.2 160 */ 161 public static final String EXTENSION_POINT_EXECUTION_ENVIRONMENTS= "executionEnvironments"; //$NON-NLS-1$ 162 163 /** 164 * Simple identifier constant (value <code>"vmInstalls"</code>) for the 165 * VM installs extension point. 166 * 167 * @since 3.2 168 */ 169 public static final String EXTENSION_POINT_VM_INSTALLS = "vmInstalls"; //$NON-NLS-1$ 170 171 /** 172 * Simple identifier constant (value <code>"libraryLocationResolvers"</code>) for the 173 * Library Resolvers extension point 174 * 175 * @since 3.7 176 */ 177 public static final String EXTENSION_POINT_LIBRARY_LOCATION_RESOLVERS = "libraryLocationResolvers"; //$NON-NLS-1$ 178 179 /** 180 * Classpath container used for a project's JRE 181 * (value <code>"org.eclipse.jdt.launching.JRE_CONTAINER"</code>). A 182 * container is resolved in the context of a specific Java project, to one 183 * or more system libraries contained in a JRE. The container can have zero 184 * or two path segments following the container name. When no segments 185 * follow the container name, the workspace default JRE is used to build a 186 * project. Otherwise the segments identify a specific JRE used to build a 187 * project: 188 * <ol> 189 * <li>VM Install Type Identifier - identifies the type of JRE used to build the 190 * project. For example, the standard VM.</li> 191 * <li>VM Install Name - a user defined name that identifies that a specific VM 192 * of the above kind. For example, <code>IBM 1.3.1</code>. This information is 193 * shared in a projects classpath file, so teams must agree on JRE naming 194 * conventions.</li> 195 * </ol> 196 * <p> 197 * Since 3.2, the path may also identify an execution environment as follows: 198 * <ol> 199 * <li>Execution environment extension point name 200 * (value <code>executionEnvironments</code>)</li> 201 * <li>Identifier of a contributed execution environment</li> 202 * </ol> 203 * @since 2.0 204 */ 205 public static final String JRE_CONTAINER = LaunchingPlugin.getUniqueIdentifier() + ".JRE_CONTAINER"; //$NON-NLS-1$ 206 207 /** 208 * Marker type identifier for JRE container problems. 209 * 210 * @since 3.6 211 */ 212 public static final String JRE_CONTAINER_MARKER = LaunchingPlugin.getUniqueIdentifier() + ".jreContainerMarker"; //$NON-NLS-1$ 213 214 /** 215 * Marker type identifier for JRE compiler compliance problems. 216 * 217 * @since 3.11 218 */ 219 public static final String JRE_COMPILER_COMPLIANCE_MARKER = LaunchingPlugin.getUniqueIdentifier() + ".jreCompilerComplianceMarker"; //$NON-NLS-1$ 220 221 /** 222 * A status code indicating that a JRE could not be resolved for a project. 223 * When a JRE cannot be resolved for a project by this plug-in's container 224 * initializer, an exception is thrown with this status code. A status handler 225 * may be registered for this status code. The <code>source</code> object provided 226 * to the status handler is the Java project for which the path could not be 227 * resolved. The status handler must return an <code>IVMInstall</code> or <code>null</code>. 228 * The container resolver will re-set the project's classpath if required. 229 * 230 * @since 2.0 231 */ 232 public static final int ERR_UNABLE_TO_RESOLVE_JRE = 160; 233 234 /** 235 * Preference key for launch/connect timeout. VM Runners should honor this timeout 236 * value when attempting to launch and connect to a debuggable VM. The value is 237 * an int, indicating a number of milliseconds. 238 * 239 * @since 2.0 240 */ 241 public static final String PREF_CONNECT_TIMEOUT = LaunchingPlugin.getUniqueIdentifier() + ".PREF_CONNECT_TIMEOUT"; //$NON-NLS-1$ 242 243 /** 244 * Preference key for the String of XML that defines all installed VMs. 245 * 246 * @since 2.1 247 */ 248 public static final String PREF_VM_XML = LaunchingPlugin.getUniqueIdentifier() + ".PREF_VM_XML"; //$NON-NLS-1$ 249 250 /** 251 * Preference key for the problem severity when an execution environment is bound to a project's build path for which there is no strictly 252 * compatible JRE available in the workspace. Value is one of {@link JavaCore#ERROR}, {@link JavaCore#WARNING}, {@link JavaCore#INFO}, or 253 * {@link JavaCore#IGNORE} 254 * 255 * @since 3.5 256 */ 257 public static final String PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE = LaunchingPlugin.getUniqueIdentifier() + ".PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE"; //$NON-NLS-1$ 258 259 /** 260 * Preference key for the problem severity when an compiler compliance that is set does not match the used JRE. Value is one of 261 * {@link JavaCore#ERROR}, {@link JavaCore#WARNING}, {@link JavaCore#INFO}, or {@link JavaCore#IGNORE} 262 * <p> 263 * This preference will not be applicable if the JRE used is 9 or above and {@link JavaCore#COMPILER_RELEASE} option is enabled. 264 * </p> 265 * 266 * @since 3.10 267 */ 268 public static final String PREF_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE = LaunchingPlugin.getUniqueIdentifier() + ".PREF_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE"; //$NON-NLS-1$ 269 270 /** 271 * Unique identifier constant (value <code>"org.eclipse.jdt.launching"</code>) 272 * for the Java launching plug-in. 273 * 274 * This preference will not be applicable if the JRE used is 9 or above and {@link JavaCore#COMPILER_RELEASE} option is enabled. 275 * @since 3.5 276 */ 277 public static final String ID_PLUGIN = LaunchingPlugin.ID_PLUGIN; 278 279 /** 280 * Default launch/connect timeout (milliseconds). 281 * 282 * @since 2.0 283 */ 284 public static final int DEF_CONNECT_TIMEOUT = 20000; 285 286 /** 287 * Attribute key for a process property. The class 288 * <code>org.eclipse.debug.core.model.IProcess</code> allows attaching 289 * String properties to processes. 290 * The value of this attribute is the command line a process 291 * was launched with. Implementers of <code>IVMRunner</code> should use 292 * this attribute key to attach the command lines to the processes they create. 293 * 294 * @deprecated - use <code>IProcess.ATTR_CMDLINE</code> 295 */ 296 @Deprecated 297 public final static String ATTR_CMDLINE= LaunchingPlugin.getUniqueIdentifier() + ".launcher.cmdLine"; //$NON-NLS-1$ 298 299 /** 300 * Boolean preference controlling whether only exported entries should even be included when the runtime classpath is computed 301 * @since 3.7 302 */ 303 public static final String PREF_ONLY_INCLUDE_EXPORTED_CLASSPATH_ENTRIES = ID_PLUGIN + ".only_include_exported_classpath_entries"; //$NON-NLS-1$ 304 305 /** 306 * Attribute key for a classpath attribute referencing a 307 * list of shared libraries that should appear on the 308 * <code>-Djava.library.path</code> system property. 309 * <p> 310 * The factory methods <code>newLibraryPathsAttribute(String[])</code> 311 * and <code>getLibraryPaths(IClasspathAttribute)</code> should be used to 312 * encode and decode the attribute value. 313 * </p> 314 * <p> 315 * Each string is used to create an <code>IPath</code> using the constructor 316 * <code>Path(String)</code>, and may contain <code>IStringVariable</code>'s. 317 * Variable substitution is performed on the string prior to constructing 318 * a path from the string. 319 * If the resulting <code>IPath</code> is a relative path, it is interpreted 320 * as relative to the workspace location. If the path is absolute, it is 321 * interpreted as an absolute path in the local file system. 322 * </p> 323 * @since 3.1 324 * @see org.eclipse.jdt.core.IClasspathAttribute 325 */ 326 public static final String CLASSPATH_ATTR_LIBRARY_PATH_ENTRY = LaunchingPlugin.getUniqueIdentifier() + ".CLASSPATH_ATTR_LIBRARY_PATH_ENTRY"; //$NON-NLS-1$ 327 328 // lock for VM initialization 329 private static Object fgVMLock = new Object(); 330 private static boolean fgInitializingVMs = false; 331 332 private static HashSet<Object> fgVMTypes = null; 333 private static String fgDefaultVMId = null; 334 private static String fgDefaultVMConnectorId = null; 335 336 /** 337 * Resolvers keyed by variable name, container id, 338 * and runtime classpath entry id. 339 */ 340 private static Map<String, IRuntimeClasspathEntryResolver> fgVariableResolvers = null; 341 private static Map<String, IRuntimeClasspathEntryResolver> fgContainerResolvers = null; 342 private static Map<String, RuntimeClasspathEntryResolver> fgRuntimeClasspathEntryResolvers = null; 343 344 /** 345 * Path providers keyed by id 346 */ 347 private static Map<String, RuntimeClasspathProvider> fgPathProviders = null; 348 349 /** 350 * Default classpath and source path providers. 351 */ 352 private static IRuntimeClasspathProvider fgDefaultClasspathProvider = new StandardClasspathProvider(); 353 private static IRuntimeClasspathProvider fgDefaultSourcePathProvider = new StandardSourcePathProvider(); 354 355 /** 356 * VM change listeners 357 */ 358 private static ListenerList<IVMInstallChangedListener> fgVMListeners = new ListenerList<>(); 359 360 /** 361 * Cache of already resolved projects in container entries. Used to avoid 362 * cycles in project dependencies when resolving classpath container entries. 363 * Counters used to know when entering/exiting to clear cache 364 */ 365 private static ThreadLocal<List<IJavaProject>> fgProjects = new ThreadLocal<>(); // Lists 366 private static ThreadLocal<Integer> fgEntryCount = new ThreadLocal<>(); // Integers 367 368 /** 369 * Set of IDs of VMs contributed via vmInstalls extension point. 370 */ 371 private static Set<String> fgContributedVMs = new HashSet<>(); 372 373 /** 374 * This class contains only static methods, and is not intended 375 * to be instantiated. 376 */ JavaRuntime()377 private JavaRuntime() { 378 } 379 380 /** 381 * Initializes VM type extensions. 382 */ initializeVMTypeExtensions()383 private static void initializeVMTypeExtensions() { 384 IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(LaunchingPlugin.ID_PLUGIN, "vmInstallTypes"); //$NON-NLS-1$ 385 if(extensionPoint != null) { 386 IConfigurationElement[] configs = extensionPoint.getConfigurationElements(); 387 MultiStatus status = new MultiStatus(LaunchingPlugin.getUniqueIdentifier(), IStatus.OK, "Exceptions occurred", null); //$NON-NLS-1$ 388 fgVMTypes = new HashSet<>(); 389 for (int i= 0; i < configs.length; i++) { 390 try { 391 fgVMTypes.add(configs[i].createExecutableExtension("class")); //$NON-NLS-1$ 392 } 393 catch (CoreException e) {status.add(e.getStatus());} 394 } 395 if (!status.isOK()) { 396 //only happens on a CoreException 397 LaunchingPlugin.log(status); 398 } 399 } 400 else { 401 LaunchingPlugin.log(new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), "VM Install extension point not found", null)); //$NON-NLS-1$ 402 } 403 } 404 405 /** 406 * Returns the VM assigned to build the given Java project. 407 * The project must exist. The VM assigned to a project is 408 * determined from its build path. 409 * 410 * @param project the project to retrieve the VM from 411 * @return the VM instance that is assigned to build the given Java project 412 * Returns <code>null</code> if no VM is referenced on the project's build path. 413 * @throws CoreException if unable to determine the project's VM install 414 */ getVMInstall(IJavaProject project)415 public static IVMInstall getVMInstall(IJavaProject project) throws CoreException { 416 // check the classpath 417 IVMInstall vm = null; 418 IClasspathEntry[] classpath = project.getRawClasspath(); 419 IRuntimeClasspathEntryResolver resolver = null; 420 IClasspathEntry entry = null; 421 for (int i = 0; i < classpath.length; i++) { 422 entry = classpath[i]; 423 switch (entry.getEntryKind()) { 424 case IClasspathEntry.CPE_VARIABLE: 425 resolver = getVariableResolver(entry.getPath().segment(0)); 426 if (resolver != null) { 427 vm = resolver.resolveVMInstall(entry); 428 } 429 break; 430 case IClasspathEntry.CPE_CONTAINER: 431 resolver = getContainerResolver(entry.getPath().segment(0)); 432 if (resolver != null) { 433 vm = resolver.resolveVMInstall(entry); 434 } 435 break; 436 } 437 if (vm != null) { 438 return vm; 439 } 440 } 441 return null; 442 } 443 444 /** 445 * Returns the VM install type with the given unique id. 446 * @param id the VM install type unique id 447 * @return The VM install type for the given id, or <code>null</code> if no 448 * VM install type with the given id is registered. 449 */ getVMInstallType(String id)450 public static IVMInstallType getVMInstallType(String id) { 451 IVMInstallType[] vmTypes= getVMInstallTypes(); 452 for (int i= 0; i < vmTypes.length; i++) { 453 if (vmTypes[i].getId().equals(id)) { 454 return vmTypes[i]; 455 } 456 } 457 return null; 458 } 459 460 /** 461 * Sets a VM as the system-wide default VM, and notifies registered VM install 462 * change listeners of the change. 463 * 464 * @param vm The VM to make the default. May be <code>null</code> to clear 465 * the default. 466 * @param monitor progress monitor or <code>null</code> 467 * @throws CoreException if trying to set the default VM install encounters problems 468 */ setDefaultVMInstall(IVMInstall vm, IProgressMonitor monitor)469 public static void setDefaultVMInstall(IVMInstall vm, IProgressMonitor monitor) throws CoreException { 470 setDefaultVMInstall(vm, monitor, true); 471 } 472 473 /** 474 * Sets a VM as the system-wide default VM, and notifies registered VM install 475 * change listeners of the change. 476 * 477 * @param vm The VM to make the default. May be <code>null</code> to clear 478 * the default. 479 * @param monitor progress monitor or <code>null</code> 480 * @param savePreference If <code>true</code>, update workbench preferences to reflect 481 * the new default VM. 482 * @throws CoreException if trying to set the default VM install encounters problems 483 * @since 2.1 484 */ setDefaultVMInstall(IVMInstall vm, IProgressMonitor monitor, boolean savePreference)485 public static void setDefaultVMInstall(IVMInstall vm, IProgressMonitor monitor, boolean savePreference) throws CoreException { 486 IVMInstall previous = null; 487 if (fgDefaultVMId != null) { 488 previous = getVMFromCompositeId(fgDefaultVMId); 489 } 490 fgDefaultVMId= getCompositeIdFromVM(vm); 491 if (savePreference) { 492 saveVMConfiguration(); 493 } 494 IVMInstall current = null; 495 if (fgDefaultVMId != null) { 496 current = getVMFromCompositeId(fgDefaultVMId); 497 } 498 if (previous != current) { 499 notifyDefaultVMChanged(previous, current); 500 } 501 } 502 503 /** 504 * Sets a VM connector as the system-wide default VM. This setting is persisted when 505 * saveVMConfiguration is called. 506 * @param connector The connector to make the default. May be <code>null</code> to clear 507 * the default. 508 * @param monitor The progress monitor to use 509 * @since 2.0 510 * @throws CoreException Thrown if saving the new default setting fails 511 */ setDefaultVMConnector(IVMConnector connector, IProgressMonitor monitor)512 public static void setDefaultVMConnector(IVMConnector connector, IProgressMonitor monitor) throws CoreException { 513 fgDefaultVMConnectorId= connector.getIdentifier(); 514 saveVMConfiguration(); 515 } 516 517 /** 518 * Return the default VM set with <code>setDefaultVM()</code>. 519 * @return Returns the default VM. May return <code>null</code> when no default 520 * VM was set or when the default VM has been disposed. 521 */ getDefaultVMInstall()522 public static IVMInstall getDefaultVMInstall() { 523 524 IVMInstall install= getVMFromCompositeId(getDefaultVMId()); 525 if (install != null) { 526 File location = install.getInstallLocation(); 527 if (location != null) { 528 if (location.exists()) { 529 return install; 530 } 531 } 532 } 533 // if the default JRE goes missing, re-detect 534 if (install != null) { 535 install.getVMInstallType().disposeVMInstall(install.getId()); 536 } 537 synchronized (fgVMLock) { 538 fgDefaultVMId = null; 539 fgVMTypes = null; 540 initializeVMs(); 541 } 542 return getVMFromCompositeId(getDefaultVMId()); 543 } 544 545 /** 546 * Return the default VM connector. 547 * @return Returns the default VM connector. 548 * @since 2.0 549 */ getDefaultVMConnector()550 public static IVMConnector getDefaultVMConnector() { 551 String id = getDefaultVMConnectorId(); 552 IVMConnector connector = null; 553 if (id != null) { 554 connector = getVMConnector(id); 555 } 556 if (connector == null) { 557 connector = new SocketAttachConnector(); 558 } 559 return connector; 560 } 561 562 /** 563 * Returns the list of registered VM types. VM types are registered via 564 * <code>"org.eclipse.jdt.launching.vmTypes"</code> extension point. 565 * Returns an empty list if there are no registered VM types. 566 * 567 * @return the list of registered VM types 568 */ getVMInstallTypes()569 public static IVMInstallType[] getVMInstallTypes() { 570 initializeVMs(); 571 return fgVMTypes.toArray(new IVMInstallType[fgVMTypes.size()]); 572 } 573 574 /** 575 * Returns the default VM id determined during the initialization of the VM types 576 * @return the id of the default VM 577 */ getDefaultVMId()578 private static String getDefaultVMId() { 579 initializeVMs(); 580 return fgDefaultVMId; 581 } 582 583 /** 584 * Returns the default VM connector id determined during the initialization of the VM types 585 * @return the id of the default VM connector 586 */ getDefaultVMConnectorId()587 private static String getDefaultVMConnectorId() { 588 initializeVMs(); 589 return fgDefaultVMConnectorId; 590 } 591 592 /** 593 * Returns a String that uniquely identifies the specified VM across all VM types. 594 * 595 * @param vm the instance of IVMInstallType to be identified 596 * @return the unique identifier for the specified VM 597 * 598 * @since 2.1 599 */ getCompositeIdFromVM(IVMInstall vm)600 public static String getCompositeIdFromVM(IVMInstall vm) { 601 if (vm == null) { 602 return null; 603 } 604 IVMInstallType vmType = vm.getVMInstallType(); 605 String typeID = vmType.getId(); 606 CompositeId id = new CompositeId(new String[] { typeID, vm.getId() }); 607 return id.toString(); 608 } 609 610 /** 611 * Return the VM corresponding to the specified composite Id. The id uniquely 612 * identifies a VM across all VM types. 613 * 614 * @param idString the composite id that specifies an instance of IVMInstall 615 * @return the VM corresponding to the specified composite Id. 616 * 617 * @since 2.1 618 */ getVMFromCompositeId(String idString)619 public static IVMInstall getVMFromCompositeId(String idString) { 620 if (idString == null || idString.length() == 0) { 621 return null; 622 } 623 CompositeId id= CompositeId.fromString(idString); 624 if (id.getPartCount() == 2) { 625 IVMInstallType vmType= getVMInstallType(id.get(0)); 626 if (vmType != null) { 627 return vmType.findVMInstall(id.get(1)); 628 } 629 } 630 return null; 631 } 632 633 /** 634 * Returns a new runtime classpath entry for the given expression that 635 * may contain string substitution variable references. The resulting expression 636 * refers to an archive (jar or directory) containing class files. 637 * 638 * @param expression an expression that resolves to the location of an archive 639 * @return runtime classpath entry 640 * @since 3.0 641 */ newStringVariableClasspathEntry(String expression)642 public static IRuntimeClasspathEntry newStringVariableClasspathEntry(String expression) { 643 return new VariableClasspathEntry(expression); 644 } 645 646 /** 647 * Returns a new runtime classpath entry containing the default classpath 648 * for the specified Java project. 649 * 650 * @param project Java project 651 * @return runtime classpath entry 652 * @since 3.0 653 */ newDefaultProjectClasspathEntry(IJavaProject project)654 public static IRuntimeClasspathEntry newDefaultProjectClasspathEntry(IJavaProject project) { 655 return new DefaultProjectClasspathEntry(project); 656 } 657 658 /** 659 * Returns a new runtime classpath entry for the given project. 660 * 661 * @param project Java project 662 * @return runtime classpath entry 663 * @since 2.0 664 */ newProjectRuntimeClasspathEntry(IJavaProject project)665 public static IRuntimeClasspathEntry newProjectRuntimeClasspathEntry(IJavaProject project) { 666 return newRuntimeClasspathEntry(JavaCore.newProjectEntry(project.getProject().getFullPath())); 667 } 668 669 /** 670 * Returns a new runtime classpath entry for the given project. 671 * 672 * @param project 673 * Java project 674 * @param classpathProperty 675 * the type of entry - one of <code>USER_CLASSES</code>, <code>BOOTSTRAP_CLASSES</code>,<code>STANDARD_CLASSES</code>, 676 * <code>MODULE_PATH</code> or <code>CLASS_PATH</code> 677 * @return runtime classpath entry 678 * @since 3.10 679 */ newProjectRuntimeClasspathEntry(IJavaProject project, int classpathProperty)680 public static IRuntimeClasspathEntry newProjectRuntimeClasspathEntry(IJavaProject project, int classpathProperty) { 681 return newRuntimeClasspathEntry(JavaCore.newProjectEntry(project.getProject().getFullPath()), classpathProperty); 682 } 683 684 685 /** 686 * Returns a new runtime classpath entry for the given archive. 687 * 688 * @param resource archive resource 689 * @return runtime classpath entry 690 * @since 2.0 691 */ newArchiveRuntimeClasspathEntry(IResource resource)692 public static IRuntimeClasspathEntry newArchiveRuntimeClasspathEntry(IResource resource) { 693 return newRuntimeClasspathEntry(JavaCore.newLibraryEntry(resource.getFullPath(), null, null)); 694 } 695 696 /** 697 * Returns a new runtime classpath entry for the given archive(possibly external). 698 * 699 * @param path 700 * absolute path to an archive 701 * @param classpathProperty 702 * the type of entry - one of <code>USER_CLASSES</code>, <code>BOOTSTRAP_CLASSES</code>,<code>STANDARD_CLASSES</code>, 703 * <code>MODULE_PATH</code> or <code>CLASS_PATH</code> 704 * @return runtime classpath entry 705 * @since 3.10 706 */ newArchiveRuntimeClasspathEntry(IPath path, int classpathProperty)707 public static IRuntimeClasspathEntry newArchiveRuntimeClasspathEntry(IPath path, int classpathProperty) { 708 return newRuntimeClasspathEntry(JavaCore.newLibraryEntry(path, null, null), classpathProperty); 709 } 710 711 /** 712 * Returns a new runtime classpath entry for the given archive(possibly external). 713 * 714 * @param path 715 * absolute path to an archive 716 * @param classpathProperty 717 * the type of entry - one of <code>USER_CLASSES</code>, <code>BOOTSTRAP_CLASSES</code>,<code>STANDARD_CLASSES</code>, 718 * <code>MODULE_PATH</code>, <code>CLASS_PATH</code> or <code>PATCH_MODULE</code> 719 * @param javaProject 720 * the javaProject to be returned by {@link IRuntimeClasspathEntry#getJavaProject()}, required for PATCH_MODULE 721 * @return runtime classpath entry 722 * @since 3.10 723 */ newArchiveRuntimeClasspathEntry(IPath path, int classpathProperty, IJavaProject javaProject)724 public static IRuntimeClasspathEntry newArchiveRuntimeClasspathEntry(IPath path, int classpathProperty, IJavaProject javaProject) { 725 RuntimeClasspathEntry entry = new RuntimeClasspathEntry(JavaCore.newLibraryEntry(path, null, null), classpathProperty); 726 entry.setJavaProject(javaProject); 727 return entry; 728 } 729 730 /** 731 * Returns a new runtime classpath entry for the given archive (possibly 732 * external). 733 * 734 * @param path absolute path to an archive 735 * @return runtime classpath entry 736 * @since 2.0 737 */ newArchiveRuntimeClasspathEntry(IPath path)738 public static IRuntimeClasspathEntry newArchiveRuntimeClasspathEntry(IPath path) { 739 return newRuntimeClasspathEntry(JavaCore.newLibraryEntry(path, null, null)); 740 } 741 742 /** 743 * Returns a new runtime classpath entry for the given archive (possibly external). 744 * 745 * @param path 746 * absolute path to an archive 747 * @param sourceAttachmentPath 748 * the absolute path of the corresponding source archive or folder, or <code>null</code> if none. Note, since 3.0, an empty path is 749 * allowed to denote no source attachment. and will be automatically converted to <code>null</code>. Since 3.4, this path can also 750 * denote a path external to the workspace. 751 * @param sourceAttachmentRootPath 752 * the location of the root of the source files within the source archive or folder or <code>null</code> if this location should be 753 * automatically detected. 754 * @param accessRules 755 * the possibly empty list of access rules for this entry 756 * @param extraAttributes 757 * the possibly empty list of extra attributes to persist with this entry 758 * @param isExported 759 * indicates whether this entry is contributed to dependent projects in addition to the output location 760 * @return runtime classpath entry 761 * @since 3.10 762 */ newArchiveRuntimeClasspathEntry(IPath path, IPath sourceAttachmentPath, IPath sourceAttachmentRootPath, IAccessRule[] accessRules, IClasspathAttribute[] extraAttributes, boolean isExported)763 public static IRuntimeClasspathEntry newArchiveRuntimeClasspathEntry(IPath path, IPath sourceAttachmentPath, IPath sourceAttachmentRootPath, IAccessRule[] accessRules, IClasspathAttribute[] extraAttributes, boolean isExported) { 764 return newRuntimeClasspathEntry(JavaCore.newLibraryEntry(path, sourceAttachmentPath, sourceAttachmentRootPath, accessRules, extraAttributes, isExported)); 765 } 766 767 /** 768 * Returns a new runtime classpath entry for the classpath 769 * variable with the given path. 770 * 771 * @param path variable path; first segment is the name of the variable; 772 * trailing segments are appended to the resolved variable value 773 * @return runtime classpath entry 774 * @since 2.0 775 */ newVariableRuntimeClasspathEntry(IPath path)776 public static IRuntimeClasspathEntry newVariableRuntimeClasspathEntry(IPath path) { 777 return newRuntimeClasspathEntry(JavaCore.newVariableEntry(path, null, null)); 778 } 779 780 /** 781 * Returns a runtime classpath entry for the given container path with the given 782 * classpath property. 783 * 784 * @param path container path 785 * @param classpathProperty the type of entry - one of <code>USER_CLASSES</code>, 786 * <code>BOOTSTRAP_CLASSES</code>,<code>STANDARD_CLASSES</code>, <code>MODULE_PATH</code> 787 * or <code>CLASS_PATH</code> 788 * @return runtime classpath entry 789 * @exception CoreException if unable to construct a runtime classpath entry 790 * @since 2.0 791 */ newRuntimeContainerClasspathEntry(IPath path, int classpathProperty)792 public static IRuntimeClasspathEntry newRuntimeContainerClasspathEntry(IPath path, int classpathProperty) throws CoreException { 793 return newRuntimeContainerClasspathEntry(path, classpathProperty, null); 794 } 795 796 /** 797 * Returns a runtime classpath entry for the given container path with the given 798 * classpath property to be resolved in the context of the given Java project. 799 * 800 * @param path container path 801 * @param classpathProperty the type of entry - one of <code>USER_CLASSES</code>, 802 * <code>BOOTSTRAP_CLASSES</code>, or <code>STANDARD_CLASSES</code> 803 * @param project Java project context used for resolution, or <code>null</code> 804 * if to be resolved in the context of the launch configuration this entry 805 * is referenced in 806 * @return runtime classpath entry 807 * @exception CoreException if unable to construct a runtime classpath entry 808 * @since 3.0 809 */ newRuntimeContainerClasspathEntry(IPath path, int classpathProperty, IJavaProject project)810 public static IRuntimeClasspathEntry newRuntimeContainerClasspathEntry(IPath path, int classpathProperty, IJavaProject project) throws CoreException { 811 RuntimeClasspathEntry entry = new RuntimeClasspathEntry(JavaCore.newContainerEntry(path), classpathProperty); 812 entry.setJavaProject(project); 813 return entry; 814 } 815 816 /** 817 * Returns a runtime classpath entry constructed from the given memento. 818 * 819 * @param memento a memento for a runtime classpath entry 820 * @return runtime classpath entry 821 * @exception CoreException if unable to construct a runtime classpath entry 822 * @since 2.0 823 */ newRuntimeClasspathEntry(String memento)824 public static IRuntimeClasspathEntry newRuntimeClasspathEntry(String memento) throws CoreException { 825 try { 826 Element root = null; 827 DocumentBuilder parser = LaunchingPlugin.getParser(); 828 StringReader reader = new StringReader(memento); 829 InputSource source = new InputSource(reader); 830 root = parser.parse(source).getDocumentElement(); 831 832 String id = root.getAttribute("id"); //$NON-NLS-1$ 833 if (id == null || id.length() == 0) { 834 // assume an old format 835 return new RuntimeClasspathEntry(root); 836 } 837 // get the extension & create a new one 838 IRuntimeClasspathEntry2 entry = LaunchingPlugin.getDefault().newRuntimeClasspathEntry(id); 839 NodeList list = root.getChildNodes(); 840 Node node = null; 841 Element element = null; 842 for (int i = 0; i < list.getLength(); i++) { 843 node = list.item(i); 844 if (node.getNodeType() == Node.ELEMENT_NODE) { 845 element = (Element)node; 846 if ("memento".equals(element.getNodeName())) { //$NON-NLS-1$ 847 entry.initializeFrom(element); 848 } 849 } 850 } 851 return entry; 852 } catch (SAXException e) { 853 abort(LaunchingMessages.JavaRuntime_32, e); 854 } catch (IOException e) { 855 abort(LaunchingMessages.JavaRuntime_32, e); 856 } 857 return null; 858 } 859 860 /** 861 * Returns a runtime classpath entry that corresponds to the given 862 * classpath entry. The classpath entry may not be of type <code>CPE_SOURCE</code> 863 * or <code>CPE_CONTAINER</code>. 864 * 865 * @param entry a classpath entry 866 * @return runtime classpath entry 867 * @since 2.0 868 */ newRuntimeClasspathEntry(IClasspathEntry entry)869 private static IRuntimeClasspathEntry newRuntimeClasspathEntry(IClasspathEntry entry) { 870 return new RuntimeClasspathEntry(entry); 871 } 872 873 /** 874 * Returns a runtime classpath entry that corresponds to the given classpath entry. The classpath entry may not be of type <code>CPE_SOURCE</code> 875 * or <code>CPE_CONTAINER</code>. 876 * 877 * @param entry 878 * a classpath entry 879 * @return runtime classpath entry 880 * @since 2.0 881 */ newRuntimeClasspathEntry(IClasspathEntry entry, int classPathProperty)882 private static IRuntimeClasspathEntry newRuntimeClasspathEntry(IClasspathEntry entry, int classPathProperty) { 883 return new RuntimeClasspathEntry(entry, classPathProperty); 884 } 885 886 /** 887 * Computes and returns the default unresolved runtime classpath for the given project. 888 * 889 * @param project 890 * the {@link IJavaProject} to compute the unresolved runtime classpath for 891 * @return runtime classpath entries 892 * @exception CoreException 893 * if unable to compute the runtime classpath 894 * @see IRuntimeClasspathEntry 895 * @since 2.0 896 */ computeUnresolvedRuntimeClasspath(IJavaProject project)897 public static IRuntimeClasspathEntry[] computeUnresolvedRuntimeClasspath(IJavaProject project) throws CoreException { 898 return computeUnresolvedRuntimeClasspath(project, false); 899 } 900 901 /** 902 * Computes and returns the default unresolved runtime classpath for the given project. 903 * 904 * @param project 905 * the {@link IJavaProject} to compute the unresolved runtime classpath for 906 * @param excludeTestCode 907 * if true, output folders corresponding to test sources and test dependencies are excluded 908 * @return runtime classpath entries 909 * @exception CoreException 910 * if unable to compute the runtime classpath 911 * @see IRuntimeClasspathEntry 912 * @since 3.10 913 */ computeUnresolvedRuntimeClasspath(IJavaProject project, boolean excludeTestCode)914 public static IRuntimeClasspathEntry[] computeUnresolvedRuntimeClasspath(IJavaProject project, boolean excludeTestCode) throws CoreException { 915 IClasspathEntry[] entries = project.getRawClasspath(); 916 List<IRuntimeClasspathEntry> classpathEntries = new ArrayList<>(3); 917 for (int i = 0; i < entries.length; i++) { 918 IClasspathEntry entry = entries[i]; 919 switch (entry.getEntryKind()) { 920 case IClasspathEntry.CPE_CONTAINER: 921 IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), project); 922 if (container != null) { 923 switch (container.getKind()) { 924 case IClasspathContainer.K_APPLICATION: 925 // don't look at application entries 926 break; 927 case IClasspathContainer.K_DEFAULT_SYSTEM: 928 classpathEntries.add(newRuntimeContainerClasspathEntry(container.getPath(), IRuntimeClasspathEntry.STANDARD_CLASSES, project)); 929 break; 930 case IClasspathContainer.K_SYSTEM: 931 classpathEntries.add(newRuntimeContainerClasspathEntry(container.getPath(), IRuntimeClasspathEntry.BOOTSTRAP_CLASSES, project)); 932 break; 933 } 934 } 935 break; 936 case IClasspathEntry.CPE_VARIABLE: 937 if (JRELIB_VARIABLE.equals(entry.getPath().segment(0))) { 938 IRuntimeClasspathEntry jre = newVariableRuntimeClasspathEntry(entry.getPath()); 939 jre.setClasspathProperty(IRuntimeClasspathEntry.STANDARD_CLASSES); 940 classpathEntries.add(jre); 941 } 942 break; 943 default: 944 break; 945 } 946 } 947 classpathEntries.add(newDefaultProjectClasspathEntry(project)); 948 return classpathEntries.toArray(new IRuntimeClasspathEntry[classpathEntries.size()]); 949 } 950 951 /** 952 * Computes and returns the default unresolved runtime classpath and modulepath for the given project. 953 * 954 * @param project 955 * the {@link IJavaProject} to compute the unresolved runtime classpath and modulepath for 956 * @return runtime classpath and modulepath entries 957 * @exception CoreException 958 * if unable to compute the runtime classpath and/or modulepath 959 * @see IRuntimeClasspathEntry 960 * @since 3.10 961 */ computeUnresolvedRuntimeDependencies(IJavaProject project)962 public static IRuntimeClasspathEntry[] computeUnresolvedRuntimeDependencies(IJavaProject project) throws CoreException { 963 return computeUnresolvedRuntimeDependencies(project, false); 964 } 965 966 /** 967 * Computes and returns the default unresolved runtime classpath and modulepath for the given project. 968 * 969 * @param project 970 * the {@link IJavaProject} to compute the unresolved runtime classpath and modulepath for 971 * @param excludeTestCode 972 * if true, output folders corresponding to test sources and test dependencies are excluded 973 * @return runtime classpath and modulepath entries 974 * @exception CoreException 975 * if unable to compute the runtime classpath and/or modulepath 976 * @see IRuntimeClasspathEntry 977 * @since 3.10 978 */ computeUnresolvedRuntimeDependencies(IJavaProject project, boolean excludeTestCode)979 public static IRuntimeClasspathEntry[] computeUnresolvedRuntimeDependencies(IJavaProject project, boolean excludeTestCode) throws CoreException { 980 IClasspathEntry entry1 = JavaCore.newProjectEntry(project.getProject().getFullPath()); 981 List<Object> classpathEntries = new ArrayList<>(5); 982 List<IClasspathEntry> expanding = new ArrayList<>(5); 983 boolean exportedEntriesOnly = Platform.getPreferencesService().getBoolean(LaunchingPlugin.ID_PLUGIN, JavaRuntime.PREF_ONLY_INCLUDE_EXPORTED_CLASSPATH_ENTRIES, false, null); 984 DefaultProjectClasspathEntry.expandProject(entry1, classpathEntries, expanding, excludeTestCode, exportedEntriesOnly, project, true); 985 IRuntimeClasspathEntry[] runtimeEntries = new IRuntimeClasspathEntry[classpathEntries.size()]; 986 for (int i = 0; i < runtimeEntries.length; i++) { 987 Object e = classpathEntries.get(i); 988 if (e instanceof IClasspathEntry) { 989 IClasspathEntry cpe = (IClasspathEntry) e; 990 if (cpe == entry1) { 991 if (isModularProject(project)) { 992 runtimeEntries[i] = new RuntimeClasspathEntry(entry1, IRuntimeClasspathEntry.MODULE_PATH); 993 } else { 994 runtimeEntries[i] = new RuntimeClasspathEntry(entry1, IRuntimeClasspathEntry.CLASS_PATH); 995 } 996 } else { 997 runtimeEntries[i] = new RuntimeClasspathEntry(cpe); 998 DefaultProjectClasspathEntry.adjustClasspathProperty(runtimeEntries[i], cpe); 999 } 1000 } else { 1001 runtimeEntries[i] = (IRuntimeClasspathEntry) e; 1002 } 1003 } 1004 List<IRuntimeClasspathEntry> ordered = new ArrayList<>(runtimeEntries.length); 1005 for (int i = 0; i < runtimeEntries.length; i++) { 1006 if (runtimeEntries[i].getClasspathProperty() != IRuntimeClasspathEntry.STANDARD_CLASSES 1007 && runtimeEntries[i].getClasspathProperty() != IRuntimeClasspathEntry.BOOTSTRAP_CLASSES) { 1008 ordered.add(runtimeEntries[i]); 1009 } 1010 } 1011 IRuntimeClasspathEntry jreEntry = JavaRuntime.computeModularJREEntry(project); 1012 if (jreEntry != null) { // With some jre stub jars don't have jre entries 1013 ordered.add(jreEntry); 1014 } 1015 return ordered.toArray(new IRuntimeClasspathEntry[ordered.size()]); 1016 } 1017 1018 /** 1019 * Checks if classpath entry is modular and project is modular . 1020 * 1021 * @param entry 1022 * the classpath entry 1023 * @return boolean <code>true</code> if entry is module else <code>false</code> 1024 * @since 3.10 1025 */ isModule(IClasspathEntry entry, IJavaProject proj)1026 public static boolean isModule(IClasspathEntry entry, IJavaProject proj) { 1027 if (entry == null) { 1028 return false; 1029 } 1030 if (!isModularProject(proj)) { 1031 return false; 1032 } 1033 1034 for (IClasspathAttribute classpathAttribute : entry.getExtraAttributes()) { 1035 if (classpathAttribute.getName().equals(IClasspathAttribute.MODULE) && "true".equals(classpathAttribute.getValue())) {//$NON-NLS-1$ 1036 return true; 1037 } 1038 } 1039 return false; 1040 1041 } 1042 1043 /** 1044 * Checks if configuration JRE is greater than 8. 1045 * 1046 * @param configuration 1047 * the launch configuration 1048 * @return boolean <code>true</code> if jre used in configuration is greater than 8 else <code>false</code> 1049 * @since 3.10 1050 */ isModularConfiguration(ILaunchConfiguration configuration)1051 public static boolean isModularConfiguration(ILaunchConfiguration configuration) { 1052 1053 try { 1054 IVMInstall vm = JavaRuntime.computeVMInstall(configuration); 1055 return isModularJava(vm); 1056 } 1057 catch (CoreException e) { 1058 e.printStackTrace(); 1059 } 1060 return false; 1061 1062 } 1063 1064 /** 1065 * Checks if vm install is modular( version greater than 8). 1066 * 1067 * @param vm 1068 * the vm install 1069 * @return boolean <code>true</code> if vm install is modular else <code>false</code> 1070 * @since 3.10 1071 */ isModularJava(IVMInstall vm)1072 public static boolean isModularJava(IVMInstall vm) { 1073 if (compareJavaVersions(vm, JavaCore.VERSION_1_8) > 0) { 1074 return true; 1075 } 1076 return false; 1077 } 1078 1079 /** 1080 * Compares the version of vm and a version of the Java platform. 1081 * 1082 * @param vm 1083 * IVMInstall to be compared 1084 * @param ver 1085 * version to be compared 1086 * @return the value {@code 0} if both versions are the same; a value less than {@code 0} if <code>vm</code> is smaller than <code>ver</code>; and 1087 * a value greater than {@code 0} if <code>vm</code> is higher than <code>ver</code>; a value {@code -1} in case of any exceptions; 1088 * 1089 * @since 3.10 1090 */ compareJavaVersions(IVMInstall vm, String ver)1091 public static int compareJavaVersions(IVMInstall vm, String ver) { 1092 if (vm instanceof AbstractVMInstall) { 1093 AbstractVMInstall install = (AbstractVMInstall) vm; 1094 String vmver = install.getJavaVersion(); 1095 if (vmver == null) { 1096 return -1; 1097 } 1098 // versionToJdkLevel only handles 3 char versions = 1.5, 1.6, 1.7, etc 1099 if (vmver.length() > 3) { 1100 vmver = vmver.substring(0, 3); 1101 } 1102 return JavaCore.compareJavaVersions(vmver, ver); 1103 } 1104 return -1; 1105 1106 } 1107 /** 1108 * Checks if project entry is modular 1109 * 1110 * @param proj 1111 * the project 1112 * @return boolean <code>true</code> if project is modular else <code>false</code> 1113 * @since 3.10 1114 */ isModularProject(IJavaProject proj)1115 public static boolean isModularProject(IJavaProject proj) { 1116 1117 IModuleDescription module; 1118 try { 1119 module = proj == null ? null : proj.getModuleDescription(); 1120 String modName = module == null ? null : module.getElementName(); 1121 if (modName != null && modName.length() > 0) { 1122 return true; 1123 } 1124 } 1125 catch (JavaModelException e) { 1126 } 1127 return false; 1128 } 1129 1130 /** 1131 * Computes and returns the unresolved source lookup path for the given launch 1132 * configuration. 1133 * 1134 * @param configuration launch configuration 1135 * @return runtime classpath entries 1136 * @exception CoreException if unable to compute the source lookup path 1137 * @since 2.0 1138 */ computeUnresolvedSourceLookupPath(ILaunchConfiguration configuration)1139 public static IRuntimeClasspathEntry[] computeUnresolvedSourceLookupPath(ILaunchConfiguration configuration) throws CoreException { 1140 return getSourceLookupPathProvider(configuration).computeUnresolvedClasspath(configuration); 1141 } 1142 1143 /** 1144 * Resolves the given source lookup path, returning the resolved source lookup path 1145 * in the context of the given launch configuration. 1146 * 1147 * @param entries unresolved entries 1148 * @param configuration launch configuration 1149 * @return resolved entries 1150 * @exception CoreException if unable to resolve the source lookup path 1151 * @since 2.0 1152 */ resolveSourceLookupPath(IRuntimeClasspathEntry[] entries, ILaunchConfiguration configuration)1153 public static IRuntimeClasspathEntry[] resolveSourceLookupPath(IRuntimeClasspathEntry[] entries, ILaunchConfiguration configuration) throws CoreException { 1154 return getSourceLookupPathProvider(configuration).resolveClasspath(entries, configuration); 1155 } 1156 1157 /** 1158 * Returns the classpath provider for the given launch configuration. 1159 * 1160 * @param configuration launch configuration 1161 * @return classpath provider 1162 * @exception CoreException if unable to resolve the path provider 1163 * @since 2.0 1164 */ getClasspathProvider(ILaunchConfiguration configuration)1165 public static IRuntimeClasspathProvider getClasspathProvider(ILaunchConfiguration configuration) throws CoreException { 1166 String providerId = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, (String)null); 1167 IRuntimeClasspathProvider provider = null; 1168 if (providerId == null) { 1169 provider = fgDefaultClasspathProvider; 1170 } else { 1171 provider = getClasspathProviders().get(providerId); 1172 if (provider == null) { 1173 abort(NLS.bind(LaunchingMessages.JavaRuntime_26, new String[]{providerId}), null); 1174 } 1175 } 1176 return provider; 1177 } 1178 1179 /** 1180 * Returns the source lookup path provider for the given launch configuration. 1181 * 1182 * @param configuration launch configuration 1183 * @return source lookup path provider 1184 * @exception CoreException if unable to resolve the path provider 1185 * @since 2.0 1186 */ getSourceLookupPathProvider(ILaunchConfiguration configuration)1187 public static IRuntimeClasspathProvider getSourceLookupPathProvider(ILaunchConfiguration configuration) throws CoreException { 1188 String providerId = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH_PROVIDER, (String)null); 1189 IRuntimeClasspathProvider provider = null; 1190 if (providerId == null) { 1191 provider = fgDefaultSourcePathProvider; 1192 } else { 1193 provider = getClasspathProviders().get(providerId); 1194 if (provider == null) { 1195 abort(NLS.bind(LaunchingMessages.JavaRuntime_27, new String[]{providerId}), null); 1196 } 1197 } 1198 return provider; 1199 } 1200 1201 /** 1202 * Returns resolved entries for the given entry in the context of the given 1203 * launch configuration. If the entry is of kind 1204 * <code>VARIABLE</code> or <code>CONTAINER</code>, variable and container 1205 * resolvers are consulted. If the entry is of kind <code>PROJECT</code>, 1206 * and the associated Java project specifies non-default output locations, 1207 * the corresponding output locations are returned. Otherwise, the given 1208 * entry is returned. 1209 * <p> 1210 * If the given entry is a variable entry, and a resolver is not registered, 1211 * the entry itself is returned. If the given entry is a container, and a 1212 * resolver is not registered, resolved runtime classpath entries are calculated 1213 * from the associated container classpath entries, in the context of the project 1214 * associated with the given launch configuration. 1215 * </p> 1216 * @param entry runtime classpath entry 1217 * @param configuration launch configuration 1218 * @return resolved runtime classpath entry 1219 * @exception CoreException if unable to resolve 1220 * @see IRuntimeClasspathEntryResolver 1221 * @since 2.0 1222 */ resolveRuntimeClasspathEntry(IRuntimeClasspathEntry entry, ILaunchConfiguration configuration)1223 public static IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClasspathEntry entry, ILaunchConfiguration configuration) throws CoreException { 1224 boolean excludeTestCode = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE, false); 1225 switch (entry.getType()) { 1226 case IRuntimeClasspathEntry.PROJECT: 1227 // if the project has multiple output locations, they must be returned 1228 IResource resource = entry.getResource(); 1229 if (resource instanceof IProject) { 1230 IProject p = (IProject)resource; 1231 IJavaProject project = JavaCore.create(p); 1232 if (project == null || !p.isOpen() || !project.exists()) { 1233 return new IRuntimeClasspathEntry[0]; 1234 } 1235 IClasspathAttribute[] attributes = entry.getClasspathEntry().getExtraAttributes(); 1236 IRuntimeClasspathEntry[] entries = resolveOutputLocations(project, entry.getClasspathProperty(), attributes, excludeTestCode); 1237 if (entries != null) { 1238 return entries; 1239 } 1240 } else { 1241 if (isOptional(entry.getClasspathEntry())) { 1242 return new IRuntimeClasspathEntry[] {}; 1243 } 1244 abort(NLS.bind(LaunchingMessages.JavaRuntime_Classpath_references_non_existant_project___0__3, new String[]{entry.getPath().lastSegment()}), null); 1245 } 1246 break; 1247 case IRuntimeClasspathEntry.VARIABLE: 1248 IRuntimeClasspathEntryResolver resolver = getVariableResolver(entry.getVariableName()); 1249 if (resolver == null) { 1250 IRuntimeClasspathEntry[] resolved = resolveVariableEntry(entry, null, false, configuration); 1251 if (resolved != null) { 1252 return resolved; 1253 } 1254 break; 1255 } 1256 return resolver.resolveRuntimeClasspathEntry(entry, configuration); 1257 case IRuntimeClasspathEntry.CONTAINER: 1258 resolver = getContainerResolver(entry.getVariableName()); 1259 if (resolver == null) { 1260 return computeDefaultContainerEntries(entry, configuration, excludeTestCode); 1261 } 1262 return resolver.resolveRuntimeClasspathEntry(entry, configuration); 1263 case IRuntimeClasspathEntry.ARCHIVE: 1264 // verify the archive exists 1265 String location = entry.getLocation(); 1266 if (location != null) { 1267 File file = new File(location); 1268 if (file.exists()) { 1269 break; 1270 } 1271 } 1272 if (isOptional(entry.getClasspathEntry())) { 1273 return new IRuntimeClasspathEntry[] {}; 1274 } 1275 abort(NLS.bind(LaunchingMessages.JavaRuntime_Classpath_references_non_existant_archive___0__4, new String[] { entry.getPath().toString() }), null); 1276 case IRuntimeClasspathEntry.OTHER: 1277 resolver = getContributedResolver(((IRuntimeClasspathEntry2)entry).getTypeId()); 1278 return resolver.resolveRuntimeClasspathEntry(entry, configuration); 1279 default: 1280 break; 1281 } 1282 return new IRuntimeClasspathEntry[] {entry}; 1283 } 1284 isOptional(IClasspathEntry entry)1285 private static boolean isOptional(IClasspathEntry entry) { 1286 IClasspathAttribute[] extraAttributes = entry.getExtraAttributes(); 1287 for (int i = 0, length = extraAttributes.length; i < length; i++) { 1288 IClasspathAttribute attribute = extraAttributes[i]; 1289 if (IClasspathAttribute.OPTIONAL.equals(attribute.getName()) && Boolean.parseBoolean(attribute.getValue())) { 1290 return true; 1291 } 1292 } 1293 return false; 1294 } 1295 1296 /** 1297 * Default resolution for a classpath variable - resolve to an archive. Only one of project/configuration can be non-null. 1298 * 1299 * @param entry 1300 * the {@link IRuntimeClasspathEntry} to try and resolve 1301 * @param project 1302 * the project context or <code>null</code> 1303 * @param excludeTestCode 1304 * if true, exclude test-code (only used if project is non-null) 1305 * @param configuration 1306 * configuration context or <code>null</code> 1307 * @return IRuntimeClasspathEntry[] 1308 * @throws CoreException 1309 * if a problem is encountered trying to resolve the given classpath entry 1310 */ resolveVariableEntry(IRuntimeClasspathEntry entry, IJavaProject project, boolean excludeTestCode, ILaunchConfiguration configuration)1311 private static IRuntimeClasspathEntry[] resolveVariableEntry(IRuntimeClasspathEntry entry, IJavaProject project, boolean excludeTestCode, ILaunchConfiguration configuration) throws CoreException { 1312 // default resolution - an archive 1313 IPath archPath = JavaCore.getClasspathVariable(entry.getVariableName()); 1314 if (archPath != null) { 1315 if (entry.getPath().segmentCount() > 1) { 1316 archPath = archPath.append(entry.getPath().removeFirstSegments(1)); 1317 } 1318 IPath srcPath = null; 1319 IPath srcVar = entry.getSourceAttachmentPath(); 1320 IPath srcRootPath = null; 1321 IPath srcRootVar = entry.getSourceAttachmentRootPath(); 1322 if (archPath != null && !archPath.isEmpty()) { 1323 if (srcVar != null && !srcVar.isEmpty()) { 1324 srcPath = JavaCore.getClasspathVariable(srcVar.segment(0)); 1325 if (srcPath != null) { 1326 if (srcVar.segmentCount() > 1) { 1327 srcPath = srcPath.append(srcVar.removeFirstSegments(1)); 1328 } 1329 if (srcRootVar != null && !srcRootVar.isEmpty()) { 1330 srcRootPath = JavaCore.getClasspathVariable(srcRootVar.segment(0)); 1331 if (srcRootPath != null) { 1332 if (srcRootVar.segmentCount() > 1) { 1333 srcRootPath = srcRootPath.append(srcRootVar.removeFirstSegments(1)); 1334 } 1335 } 1336 } 1337 } 1338 } 1339 // now resolve the archive (recursively) 1340 IClasspathEntry cpEntry = entry.getClasspathEntry(); 1341 IClasspathEntry archEntry = JavaCore.newLibraryEntry(archPath, srcPath, srcRootPath, null, cpEntry.getExtraAttributes(), cpEntry.isExported()); 1342 IRuntimeClasspathEntry runtimeArchEntry = newRuntimeClasspathEntry(archEntry); 1343 runtimeArchEntry.setClasspathProperty(entry.getClasspathProperty()); 1344 if (configuration == null) { 1345 return resolveRuntimeClasspathEntry(runtimeArchEntry, project, excludeTestCode); 1346 } 1347 return resolveRuntimeClasspathEntry(runtimeArchEntry, configuration); 1348 } 1349 } 1350 return null; 1351 } 1352 1353 /** 1354 * Returns runtime classpath entries corresponding to the output locations of the given project, or null if the project only uses the default 1355 * output location. 1356 * 1357 * @param project 1358 * the {@link IJavaProject} to resolve the output locations for 1359 * @param classpathProperty 1360 * the type of classpath entries to create 1361 * @param attributes 1362 * extra attributes of the original classpath entry 1363 * @param excludeTestCode 1364 * if true, output folders corresponding to test sources are excluded 1365 * 1366 * @return IRuntimeClasspathEntry[] or <code>null</code> 1367 * @throws CoreException 1368 * if output resolution encounters a problem 1369 */ resolveOutputLocations(IJavaProject project, int classpathProperty, IClasspathAttribute[] attributes, boolean excludeTestCode)1370 private static IRuntimeClasspathEntry[] resolveOutputLocations(IJavaProject project, int classpathProperty, IClasspathAttribute[] attributes, boolean excludeTestCode) throws CoreException { 1371 List<IPath> nonDefault = new ArrayList<>(); 1372 boolean defaultUsedByNonTest = false; 1373 if (project.exists() && project.getProject().isOpen()) { 1374 IClasspathEntry entries[] = project.getRawClasspath(); 1375 for (int i = 0; i < entries.length; i++) { 1376 IClasspathEntry classpathEntry = entries[i]; 1377 if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { 1378 IPath path = classpathEntry.getOutputLocation(); 1379 if (path != null) { 1380 if (!(excludeTestCode && classpathEntry.isTest())) { 1381 nonDefault.add(path); 1382 } 1383 } else { 1384 if (!classpathEntry.isTest()) { 1385 defaultUsedByNonTest = true; 1386 } 1387 } 1388 } 1389 } 1390 } 1391 boolean isModular = project.getOwnModuleDescription() != null; 1392 if (nonDefault.isEmpty() && !isModular && !excludeTestCode) { 1393 // return here only if non-modular, because patch-module might be needed otherwise 1394 return null; 1395 } 1396 // add the default location if not already included 1397 IPath def = project.getOutputLocation(); 1398 if (!excludeTestCode || defaultUsedByNonTest) { 1399 if (!nonDefault.contains(def)) { 1400 nonDefault.add(def); 1401 } 1402 } 1403 IRuntimeClasspathEntry[] locations = new IRuntimeClasspathEntry[nonDefault.size()]; 1404 for (int i = 0; i < locations.length; i++) { 1405 IClasspathEntry newEntry = JavaCore.newLibraryEntry(nonDefault.get(i), null, null, null, attributes, false); 1406 locations[i] = new RuntimeClasspathEntry(newEntry); 1407 if (isModular && !containsModuleInfo(locations[i])) { 1408 locations[i].setClasspathProperty(IRuntimeClasspathEntry.PATCH_MODULE); 1409 ((RuntimeClasspathEntry) locations[i]).setJavaProject(project); 1410 } else { 1411 locations[i].setClasspathProperty(classpathProperty); 1412 } 1413 } 1414 return locations; 1415 } 1416 containsModuleInfo(IRuntimeClasspathEntry entry)1417 private static boolean containsModuleInfo(IRuntimeClasspathEntry entry) { 1418 return new File(entry.getLocation() + File.separator + "module-info.class").exists(); //$NON-NLS-1$ 1419 } 1420 1421 /** 1422 * Returns resolved entries for the given entry in the context of the given 1423 * Java project. If the entry is of kind 1424 * <code>VARIABLE</code> or <code>CONTAINER</code>, variable and container 1425 * resolvers are consulted. If the entry is of kind <code>PROJECT</code>, 1426 * and the associated Java project specifies non-default output locations, 1427 * the corresponding output locations are returned. Otherwise, the given 1428 * entry is returned. 1429 * <p> 1430 * If the given entry is a variable entry, and a resolver is not registered, 1431 * the entry itself is returned. If the given entry is a container, and a 1432 * resolver is not registered, resolved runtime classpath entries are calculated 1433 * from the associated container classpath entries, in the context of the 1434 * given project. 1435 * </p> 1436 * @param entry runtime classpath entry 1437 * @param project Java project context 1438 * @return resolved runtime classpath entry 1439 * @exception CoreException if unable to resolve 1440 * @see IRuntimeClasspathEntryResolver 1441 * @since 2.0 1442 */ resolveRuntimeClasspathEntry(IRuntimeClasspathEntry entry, IJavaProject project)1443 public static IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClasspathEntry entry, IJavaProject project) throws CoreException { 1444 return resolveRuntimeClasspathEntry(entry, project, false); 1445 } 1446 1447 /** 1448 * Returns resolved entries for the given entry in the context of the given 1449 * Java project. If the entry is of kind 1450 * <code>VARIABLE</code> or <code>CONTAINER</code>, variable and container 1451 * resolvers are consulted. If the entry is of kind <code>PROJECT</code>, 1452 * and the associated Java project specifies non-default output locations, 1453 * the corresponding output locations are returned. Otherwise, the given 1454 * entry is returned. 1455 * <p> 1456 * If the given entry is a variable entry, and a resolver is not registered, 1457 * the entry itself is returned. If the given entry is a container, and a 1458 * resolver is not registered, resolved runtime classpath entries are calculated 1459 * from the associated container classpath entries, in the context of the 1460 * given project. 1461 * </p> 1462 * @param entry runtime classpath entry 1463 * @param project Java project context 1464 * @param excludeTestCode 1465 * if true, output folders corresponding to test sources and test dependencies are excluded 1466 * @return resolved runtime classpath entry 1467 * @exception CoreException if unable to resolve 1468 * @see IRuntimeClasspathEntryResolver 1469 * @since 3.10 1470 */ resolveRuntimeClasspathEntry(IRuntimeClasspathEntry entry, IJavaProject project, boolean excludeTestCode)1471 public static IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClasspathEntry entry, IJavaProject project, boolean excludeTestCode) throws CoreException { 1472 switch (entry.getType()) { 1473 case IRuntimeClasspathEntry.PROJECT: 1474 // if the project has multiple output locations, they must be returned 1475 IResource resource = entry.getResource(); 1476 if (resource instanceof IProject) { 1477 IProject p = (IProject)resource; 1478 IJavaProject jp = JavaCore.create(p); 1479 if (jp != null && p.isOpen() && jp.exists()) { 1480 IClasspathAttribute[] attributes = entry.getClasspathEntry().getExtraAttributes(); 1481 IRuntimeClasspathEntry[] entries = resolveOutputLocations(jp, entry.getClasspathProperty(), attributes, excludeTestCode); 1482 if (entries != null) { 1483 return entries; 1484 } 1485 } else { 1486 return new IRuntimeClasspathEntry[0]; 1487 } 1488 } 1489 break; 1490 case IRuntimeClasspathEntry.VARIABLE: 1491 IRuntimeClasspathEntryResolver resolver = getVariableResolver(entry.getVariableName()); 1492 if (resolver == null) { 1493 IRuntimeClasspathEntry[] resolved = resolveVariableEntry(entry, project, excludeTestCode, null); 1494 if (resolved != null) { 1495 return resolved; 1496 } 1497 break; 1498 } 1499 return resolver.resolveRuntimeClasspathEntry(entry, project, excludeTestCode); 1500 case IRuntimeClasspathEntry.CONTAINER: 1501 resolver = getContainerResolver(entry.getVariableName()); 1502 if (resolver == null) { 1503 return computeDefaultContainerEntries(entry, project, excludeTestCode); 1504 } 1505 return resolver.resolveRuntimeClasspathEntry(entry, project, excludeTestCode); 1506 case IRuntimeClasspathEntry.OTHER: 1507 resolver = getContributedResolver(((IRuntimeClasspathEntry2)entry).getTypeId()); 1508 return resolver.resolveRuntimeClasspathEntry(entry, project, excludeTestCode); 1509 default: 1510 break; 1511 } 1512 return new IRuntimeClasspathEntry[] {entry}; 1513 } 1514 1515 /** 1516 * Performs default resolution for a container entry. Delegates to the Java model. 1517 * 1518 * @param entry 1519 * the {@link IRuntimeClasspathEntry} to compute default container entries for 1520 * @param config 1521 * the backing {@link ILaunchConfiguration} 1522 * @param excludeTestCode 1523 * if true, output folders corresponding to test sources and test dependencies are excluded 1524 * @return the complete listing of default container entries or an empty list, never <code>null</code> 1525 * @throws CoreException 1526 * if the computation encounters a problem 1527 */ computeDefaultContainerEntries(IRuntimeClasspathEntry entry, ILaunchConfiguration config, boolean excludeTestCode)1528 private static IRuntimeClasspathEntry[] computeDefaultContainerEntries(IRuntimeClasspathEntry entry, ILaunchConfiguration config, boolean excludeTestCode) throws CoreException { 1529 IJavaProject project = entry.getJavaProject(); 1530 if (project == null) { 1531 project = getJavaProject(config); 1532 } 1533 return computeDefaultContainerEntries(entry, project, excludeTestCode); 1534 } 1535 1536 /** 1537 * Performs default resolution for a container entry. Delegates to the Java model. 1538 * 1539 * @param entry 1540 * the {@link IRuntimeClasspathEntry} to compute default container entries for 1541 * @param project 1542 * the backing {@link IJavaProject} 1543 * @param excludeTestCode 1544 * if true, output folders corresponding to test sources and test dependencies are excluded 1545 * @return the complete listing of default container entries or an empty list, never <code>null</code> 1546 * @throws CoreException 1547 * if the computation encounters a problem 1548 */ computeDefaultContainerEntries(IRuntimeClasspathEntry entry, IJavaProject project, boolean excludeTestCode)1549 private static IRuntimeClasspathEntry[] computeDefaultContainerEntries(IRuntimeClasspathEntry entry, IJavaProject project, boolean excludeTestCode) throws CoreException { 1550 if (project == null || entry == null) { 1551 // cannot resolve without entry or project context 1552 return new IRuntimeClasspathEntry[0]; 1553 } 1554 IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), project); 1555 if (container == null) { 1556 abort(NLS.bind(LaunchingMessages.JavaRuntime_Could_not_resolve_classpath_container___0__1, new String[]{entry.getPath().toString()}), null); 1557 // execution will not reach here - exception will be thrown 1558 return null; 1559 } 1560 IClasspathEntry[] cpes = container.getClasspathEntries(); 1561 int property = -1; 1562 switch (container.getKind()) { 1563 case IClasspathContainer.K_APPLICATION: 1564 switch (entry.getClasspathProperty()) { 1565 case IRuntimeClasspathEntry.MODULE_PATH: 1566 property = IRuntimeClasspathEntry.MODULE_PATH; 1567 break; 1568 case IRuntimeClasspathEntry.CLASS_PATH: 1569 property = IRuntimeClasspathEntry.CLASS_PATH; 1570 break; 1571 default: 1572 property = IRuntimeClasspathEntry.USER_CLASSES; 1573 break; 1574 } 1575 break; 1576 1577 case IClasspathContainer.K_DEFAULT_SYSTEM: 1578 property = IRuntimeClasspathEntry.STANDARD_CLASSES; 1579 break; 1580 case IClasspathContainer.K_SYSTEM: 1581 property = IRuntimeClasspathEntry.BOOTSTRAP_CLASSES; 1582 break; 1583 } 1584 List<IRuntimeClasspathEntry> resolved = new ArrayList<>(cpes.length); 1585 List<IJavaProject> projects = fgProjects.get(); 1586 Integer count = fgEntryCount.get(); 1587 if (projects == null) { 1588 projects = new ArrayList<>(); 1589 fgProjects.set(projects); 1590 count = Integer.valueOf(0); 1591 } 1592 int intCount = count.intValue(); 1593 intCount++; 1594 fgEntryCount.set(Integer.valueOf(intCount)); 1595 try { 1596 for (int i = 0; i < cpes.length; i++) { 1597 IClasspathEntry cpe = cpes[i]; 1598 if (cpe.getEntryKind() == IClasspathEntry.CPE_PROJECT) { 1599 IProject p = ResourcesPlugin.getWorkspace().getRoot().getProject(cpe.getPath().segment(0)); 1600 IJavaProject jp = JavaCore.create(p); 1601 if (!projects.contains(jp)) { 1602 projects.add(jp); 1603 IRuntimeClasspathEntry classpath = newDefaultProjectClasspathEntry(jp); 1604 IRuntimeClasspathEntry[] entries = resolveRuntimeClasspathEntry(classpath, jp, excludeTestCode); 1605 for (int j = 0; j < entries.length; j++) { 1606 IRuntimeClasspathEntry e = entries[j]; 1607 if (!resolved.contains(e)) { 1608 resolved.add(entries[j]); 1609 } 1610 } 1611 } 1612 } else { 1613 IRuntimeClasspathEntry e = newRuntimeClasspathEntry(cpe); 1614 if (!resolved.contains(e)) { 1615 resolved.add(e); 1616 } 1617 } 1618 } 1619 } finally { 1620 intCount--; 1621 if (intCount == 0) { 1622 fgProjects.set(null); 1623 fgEntryCount.set(null); 1624 } else { 1625 fgEntryCount.set(Integer.valueOf(intCount)); 1626 } 1627 } 1628 // set classpath property 1629 IRuntimeClasspathEntry[] result = new IRuntimeClasspathEntry[resolved.size()]; 1630 for (int i = 0; i < result.length; i++) { 1631 result[i] = resolved.get(i); 1632 result[i].setClasspathProperty(property); 1633 } 1634 return result; 1635 } 1636 1637 /** 1638 * Computes and returns the unresolved class path for the given launch configuration. 1639 * Variable and container entries are unresolved. 1640 * 1641 * @param configuration launch configuration 1642 * @return unresolved runtime classpath entries 1643 * @exception CoreException if unable to compute the classpath 1644 * @since 2.0 1645 */ computeUnresolvedRuntimeClasspath(ILaunchConfiguration configuration)1646 public static IRuntimeClasspathEntry[] computeUnresolvedRuntimeClasspath(ILaunchConfiguration configuration) throws CoreException { 1647 return getClasspathProvider(configuration).computeUnresolvedClasspath(configuration); 1648 } 1649 1650 /** 1651 * Resolves the given classpath, returning the resolved classpath 1652 * in the context of the given launch configuration. 1653 * 1654 * @param entries unresolved classpath 1655 * @param configuration launch configuration 1656 * @return resolved runtime classpath entries 1657 * @exception CoreException if unable to compute the classpath 1658 * @since 2.0 1659 */ resolveRuntimeClasspath(IRuntimeClasspathEntry[] entries, ILaunchConfiguration configuration)1660 public static IRuntimeClasspathEntry[] resolveRuntimeClasspath(IRuntimeClasspathEntry[] entries, ILaunchConfiguration configuration) throws CoreException { 1661 if (isModularConfiguration(configuration)) { 1662 IRuntimeClasspathEntry[] entries1 = getClasspathProvider(configuration).resolveClasspath(entries, configuration); 1663 ArrayList<IRuntimeClasspathEntry> entries2 = new ArrayList<>(entries1.length); 1664 for (IRuntimeClasspathEntry entry : entries1) { 1665 switch (entry.getClasspathEntry().getEntryKind()) { 1666 case IClasspathEntry.CPE_LIBRARY: 1667 try { 1668 IJavaProject project = JavaRuntime.getJavaProject(configuration); 1669 if (project == null) { 1670 entries2.add(entry); 1671 } 1672 else { 1673 IPackageFragmentRoot root = project.findPackageFragmentRoot(entry.getPath()); 1674 if (root == null && !entry.getPath().lastSegment().contains("jrt-fs.jar")) { //$NON-NLS-1$ 1675 entries2.add(entry); 1676 } else if (root != null && !root.getRawClasspathEntry().getPath().segment(0).contains("JRE_CONTAINER")) { //$NON-NLS-1$ 1677 entries2.add(entry); 1678 } 1679 } 1680 } 1681 catch (CoreException ex) { 1682 // Not a java project 1683 if (!entry.getPath().lastSegment().contains("jrt-fs.jar")) { //$NON-NLS-1$ 1684 entries2.add(entry); 1685 } 1686 } 1687 break; 1688 default: 1689 entries2.add(entry); 1690 1691 } 1692 } 1693 return entries2.toArray(new IRuntimeClasspathEntry[entries2.size()]); 1694 } 1695 return getClasspathProvider(configuration).resolveClasspath(entries, configuration); 1696 } 1697 1698 /** 1699 * Return the <code>IJavaProject</code> referenced in the specified configuration or 1700 * <code>null</code> if none. This method looks for the existence of the {@link IJavaLaunchConfigurationConstants#ATTR_PROJECT_NAME} 1701 * attribute in the given configuration. 1702 * 1703 * @param configuration the {@link ILaunchConfiguration} to try and compute the {@link IJavaProject} from 1704 * @return the referenced {@link IJavaProject} or <code>null</code> 1705 * @exception CoreException if the referenced Java project does not exist 1706 * @since 2.0 1707 */ getJavaProject(ILaunchConfiguration configuration)1708 public static IJavaProject getJavaProject(ILaunchConfiguration configuration) throws CoreException { 1709 String projectName = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null); 1710 if ((projectName == null) || (projectName.trim().length() < 1)) { 1711 return null; 1712 } 1713 IJavaProject javaProject = getJavaModel().getJavaProject(projectName); 1714 if (javaProject != null && javaProject.getProject().exists() && !javaProject.getProject().isOpen()) { 1715 abort(NLS.bind(LaunchingMessages.JavaRuntime_28, new String[] {configuration.getName(), projectName}), IJavaLaunchConfigurationConstants.ERR_PROJECT_CLOSED, null); 1716 } 1717 if ((javaProject == null) || !javaProject.exists()) { 1718 abort(NLS.bind(LaunchingMessages.JavaRuntime_Launch_configuration__0__references_non_existing_project__1___1, new String[] {configuration.getName(), projectName}), IJavaLaunchConfigurationConstants.ERR_NOT_A_JAVA_PROJECT, null); 1719 } 1720 return javaProject; 1721 } 1722 1723 /** 1724 * Convenience method to get the java model. 1725 * @return the {@link IJavaModel} made against the {@link IWorkspaceRoot} 1726 */ getJavaModel()1727 private static IJavaModel getJavaModel() { 1728 return JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()); 1729 } 1730 1731 /** 1732 * Returns the VM install for the given launch configuration. 1733 * The VM install is determined in the following prioritized way: 1734 * <ol> 1735 * <li>The VM install is explicitly specified on the launch configuration 1736 * via the <code>ATTR_JRE_CONTAINER_PATH</code> attribute (since 3.2).</li> 1737 * <li>The VM install is explicitly specified on the launch configuration 1738 * via the <code>ATTR_VM_INSTALL_TYPE</code> and <code>ATTR_VM_INSTALL_ID</code> 1739 * attributes.</li> 1740 * <li>If no explicit VM install is specified, the VM install associated with 1741 * the launch configuration's project is returned.</li> 1742 * <li>If no project is specified, or the project does not specify a custom 1743 * VM install, the workspace default VM install is returned.</li> 1744 * </ol> 1745 * 1746 * @param configuration launch configuration 1747 * @return VM install 1748 * @exception CoreException if unable to compute a VM install 1749 * @since 2.0 1750 */ computeVMInstall(ILaunchConfiguration configuration)1751 public static IVMInstall computeVMInstall(ILaunchConfiguration configuration) throws CoreException { 1752 String jreAttr = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, (String)null); 1753 if (jreAttr == null) { 1754 @SuppressWarnings("deprecation") 1755 String type = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_TYPE, (String)null); 1756 if (type == null) { 1757 IJavaProject proj = getJavaProject(configuration); 1758 if (proj != null) { 1759 IVMInstall vm = getVMInstall(proj); 1760 if (vm != null) { 1761 return vm; 1762 } 1763 } 1764 } else { 1765 @SuppressWarnings("deprecation") 1766 String name = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_NAME, (String)null); 1767 return resolveVM(type, name, configuration); 1768 } 1769 } else { 1770 IPath jrePath = Path.fromPortableString(jreAttr); 1771 IClasspathEntry entry = JavaCore.newContainerEntry(jrePath); 1772 IRuntimeClasspathEntryResolver2 resolver = getVariableResolver(jrePath.segment(0)); 1773 if (resolver != null) { 1774 return resolver.resolveVMInstall(entry); 1775 } 1776 resolver = getContainerResolver(jrePath.segment(0)); 1777 if (resolver != null) { 1778 return resolver.resolveVMInstall(entry); 1779 } 1780 } 1781 1782 return getDefaultVMInstall(); 1783 } 1784 /** 1785 * Returns the VM of the given type with the specified name. 1786 * 1787 * @param type VM type identifier 1788 * @param name VM name 1789 * @param configuration the backing {@link ILaunchConfiguration} 1790 * @return VM install 1791 * @exception CoreException if unable to resolve 1792 * @since 3.2 1793 */ resolveVM(String type, String name, ILaunchConfiguration configuration)1794 private static IVMInstall resolveVM(String type, String name, ILaunchConfiguration configuration) throws CoreException { 1795 IVMInstallType vt = getVMInstallType(type); 1796 if (vt == null) { 1797 // error type does not exist 1798 abort(NLS.bind(LaunchingMessages.JavaRuntime_Specified_VM_install_type_does_not_exist___0__2, new String[] {type}), null); 1799 } 1800 IVMInstall vm = null; 1801 // look for a name 1802 if (name == null) { 1803 // error - type specified without a specific install (could be an old config that specified a VM ID) 1804 // log the error, but choose the default VM. 1805 LaunchingPlugin.log(new Status(IStatus.WARNING, LaunchingPlugin.getUniqueIdentifier(), IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_VM_INSTALL, NLS.bind("VM not fully specified in launch configuration {0} - missing VM name. Reverting to default VM.", new String[] {configuration.getName()}), null)); //$NON-NLS-1$ 1806 return getDefaultVMInstall(); 1807 } 1808 vm = vt.findVMInstallByName(name); 1809 if (vm == null) { 1810 // error - install not found 1811 abort(NLS.bind(LaunchingMessages.JavaRuntime_Specified_VM_install_not_found__type__0___name__1__2, new String[] {vt.getName(), name}), null); 1812 } else { 1813 return vm; 1814 } 1815 // won't reach here 1816 return null; 1817 } 1818 1819 /** 1820 * Throws a core exception with an internal error status. 1821 * 1822 * @param message the status message 1823 * @param exception lower level exception associated with the 1824 * error, or <code>null</code> if none 1825 * @throws CoreException a {@link CoreException} wrapper 1826 */ abort(String message, Throwable exception)1827 private static void abort(String message, Throwable exception) throws CoreException { 1828 abort(message, IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR, exception); 1829 } 1830 1831 1832 /** 1833 * Throws a core exception with an internal error status. 1834 * 1835 * @param message the status message 1836 * @param code status code 1837 * @param exception lower level exception associated with the 1838 * 1839 * error, or <code>null</code> if none 1840 * @throws CoreException a {@link CoreException} wrapper 1841 */ abort(String message, int code, Throwable exception)1842 private static void abort(String message, int code, Throwable exception) throws CoreException { 1843 throw new CoreException(new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), code, message, exception)); 1844 } 1845 1846 /** 1847 * Computes the default application classpath entries for the given 1848 * project. 1849 * 1850 * @param jproject The project to compute the classpath for 1851 * @return The computed classpath. May be empty, but not null. 1852 * @throws CoreException if unable to compute the default classpath 1853 */ computeDefaultRuntimeClassPath(IJavaProject jproject)1854 public static String[] computeDefaultRuntimeClassPath(IJavaProject jproject) throws CoreException { 1855 IRuntimeClasspathEntry[] unresolved = computeUnresolvedRuntimeClasspath(jproject); 1856 // 1. remove bootpath entries 1857 // 2. resolve & translate to local file system paths 1858 List<String> resolved = new ArrayList<>(unresolved.length); 1859 for (int i = 0; i < unresolved.length; i++) { 1860 IRuntimeClasspathEntry entry = unresolved[i]; 1861 if (entry.getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES) { 1862 IRuntimeClasspathEntry[] entries = resolveRuntimeClasspathEntry(entry, jproject); 1863 for (int j = 0; j < entries.length; j++) { 1864 String location = entries[j].getLocation(); 1865 if (location != null) { 1866 resolved.add(location); 1867 } 1868 } 1869 } 1870 } 1871 return resolved.toArray(new String[resolved.size()]); 1872 } 1873 1874 /** 1875 * Saves the VM configuration information to the preferences. This includes the following information: 1876 * <ul> 1877 * <li>The list of all defined IVMInstall instances.</li> 1878 * <li>The default VM</li> 1879 * </ul> 1880 * This state will be read again upon first access to VM configuration information. 1881 * 1882 * @throws CoreException 1883 * if trying to save the current state of VMs encounters a problem 1884 */ saveVMConfiguration()1885 public static void saveVMConfiguration() throws CoreException { 1886 if (fgVMTypes == null) { 1887 // if the VM types have not been instantiated, there can be no changes. 1888 return; 1889 } 1890 String xml = getVMsAsXML(); 1891 InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN).put(PREF_VM_XML, xml); 1892 savePreferences(); 1893 } 1894 1895 /** 1896 * Returns the listing of currently installed VMs as a single XML file 1897 * @return an XML representation of all of the currently installed VMs 1898 * @throws CoreException if trying to compute the XML for the VM state encounters a problem 1899 */ getVMsAsXML()1900 private static String getVMsAsXML() throws CoreException { 1901 VMDefinitionsContainer container = new VMDefinitionsContainer(); 1902 container.setDefaultVMInstallCompositeID(getDefaultVMId()); 1903 container.setDefaultVMInstallConnectorTypeID(getDefaultVMConnectorId()); 1904 IVMInstallType[] vmTypes = getVMInstallTypes(); 1905 IVMInstall[] vms = null; 1906 for (int i = 0; i < vmTypes.length; ++i) { 1907 vms = vmTypes[i].getVMInstalls(); 1908 for (int j = 0; j < vms.length; j++) { 1909 container.addVM(vms[j]); 1910 } 1911 } 1912 return container.getAsXML(); 1913 } 1914 1915 /** 1916 * This method loads installed JREs based an existing user preference 1917 * or old VM configurations file. The VMs found in the preference 1918 * or VM configurations file are added to the given VM definitions container. 1919 * 1920 * Returns whether the user preferences should be set - i.e. if it was 1921 * not already set when initialized. 1922 * @param vmDefs the container to add persisted VM information to 1923 * @return whether the user preferences should be set 1924 * @throws IOException if reading the {@link #PREF_VM_XML} preference stream encounters a problem 1925 */ addPersistedVMs(VMDefinitionsContainer vmDefs)1926 private static boolean addPersistedVMs(VMDefinitionsContainer vmDefs) throws IOException { 1927 // Try retrieving the VM preferences from the preference store 1928 String vmXMLString = InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN).get(PREF_VM_XML, ""); //$NON-NLS-1$ 1929 1930 // If the preference was found, load VMs from it into memory 1931 if (vmXMLString.length() > 0) { 1932 try { 1933 ByteArrayInputStream inputStream = new ByteArrayInputStream(vmXMLString.getBytes("UTF8")); //$NON-NLS-1$ 1934 VMDefinitionsContainer.parseXMLIntoContainer(inputStream, vmDefs); 1935 return false; 1936 } catch (IOException ioe) { 1937 LaunchingPlugin.log(ioe); 1938 } 1939 } else { 1940 // Otherwise, look for the old file that previously held the VM definitions 1941 IPath stateLocation= LaunchingPlugin.getDefault().getStateLocation(); 1942 IPath stateFile= stateLocation.append("vmConfiguration.xml"); //$NON-NLS-1$ 1943 File file = new File(stateFile.toOSString()); 1944 1945 if (file.exists()) { 1946 // If file exists, load VM definitions from it into memory and write the definitions to 1947 // the preference store WITHOUT triggering any processing of the new value 1948 InputStream fileInputStream = new BufferedInputStream(new FileInputStream(file)); 1949 VMDefinitionsContainer.parseXMLIntoContainer(fileInputStream, vmDefs); 1950 } 1951 } 1952 return true; 1953 } 1954 1955 /** 1956 * Loads contributed VM installs 1957 * @param vmDefs the container to add contributed VM install information to 1958 * @since 3.2 1959 */ addVMExtensions(VMDefinitionsContainer vmDefs)1960 private static void addVMExtensions(VMDefinitionsContainer vmDefs) { 1961 IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(LaunchingPlugin.ID_PLUGIN, JavaRuntime.EXTENSION_POINT_VM_INSTALLS); 1962 IConfigurationElement[] configs= extensionPoint.getConfigurationElements(); 1963 for (int i = 0; i < configs.length; i++) { 1964 IConfigurationElement element = configs[i]; 1965 try { 1966 if ("vmInstall".equals(element.getName())) { //$NON-NLS-1$ 1967 String vmType = element.getAttribute("vmInstallType"); //$NON-NLS-1$ 1968 if (vmType == null) { 1969 abort(NLS.bind("Missing required vmInstallType attribute for vmInstall contributed by {0}", //$NON-NLS-1$ 1970 new String[]{element.getContributor().getName()}), null); 1971 } 1972 String id = element.getAttribute("id"); //$NON-NLS-1$ 1973 if (id == null) { 1974 abort(NLS.bind("Missing required id attribute for vmInstall contributed by {0}", //$NON-NLS-1$ 1975 new String[]{element.getContributor().getName()}), null); 1976 } 1977 IVMInstallType installType = getVMInstallType(vmType); 1978 if (installType == null) { 1979 abort(NLS.bind("vmInstall {0} contributed by {1} references undefined VM install type {2}", //$NON-NLS-1$ 1980 new String[]{id, element.getContributor().getName(), vmType}), null); 1981 } 1982 IVMInstall install = installType.findVMInstall(id); 1983 if (install == null) { 1984 // only load/create if first time we've seen this VM install 1985 String name = element.getAttribute("name"); //$NON-NLS-1$ 1986 if (name == null) { 1987 abort(NLS.bind("vmInstall {0} contributed by {1} missing required attribute name", //$NON-NLS-1$ 1988 new String[]{id, element.getContributor().getName()}), null); 1989 } 1990 String home = element.getAttribute("home"); //$NON-NLS-1$ 1991 if (home == null) { 1992 abort(NLS.bind("vmInstall {0} contributed by {1} missing required attribute home", //$NON-NLS-1$ 1993 new String[]{id, element.getContributor().getName()}), null); 1994 } 1995 String javadoc = element.getAttribute("javadocURL"); //$NON-NLS-1$ 1996 String vmArgs = element.getAttribute("vmArgs"); //$NON-NLS-1$ 1997 VMStandin standin = null; 1998 home = substitute(home); 1999 File homeDir = new File(home); 2000 if (homeDir.exists()) { 2001 try { 2002 // adjust for relative path names 2003 home = homeDir.getCanonicalPath(); 2004 homeDir = new File(home); 2005 } catch (IOException e) { 2006 } 2007 } 2008 if (EEVMType.ID_EE_VM_TYPE.equals(installType.getId())) { 2009 standin = createVMFromDefinitionFile(homeDir, name, id); 2010 } else { 2011 standin = new VMStandin(installType, id); 2012 standin.setName(name); 2013 IStatus status = installType.validateInstallLocation(homeDir); 2014 if (!status.isOK()) { 2015 abort(NLS.bind("Illegal install location {0} for vmInstall {1} contributed by {2}: {3}", //$NON-NLS-1$ 2016 new String[]{home, id, element.getContributor().getName(), status.getMessage()}), null); 2017 } 2018 standin.setInstallLocation(homeDir); 2019 if (javadoc != null) { 2020 try { 2021 standin.setJavadocLocation(new URL(javadoc)); 2022 } catch (MalformedURLException e) { 2023 abort(NLS.bind("Illegal javadocURL attribute for vmInstall {0} contributed by {1}", //$NON-NLS-1$ 2024 new String[]{id, element.getContributor().getName()}), e); 2025 } 2026 } 2027 // allow default arguments to be specified by VM install type if no explicit arguments 2028 if (vmArgs == null) { 2029 if (installType instanceof AbstractVMInstallType) { 2030 AbstractVMInstallType type = (AbstractVMInstallType) installType; 2031 vmArgs = type.getDefaultVMArguments(homeDir); 2032 } 2033 } 2034 if (vmArgs != null) { 2035 standin.setVMArgs(vmArgs); 2036 } 2037 IConfigurationElement[] libraries = element.getChildren("library"); //$NON-NLS-1$ 2038 LibraryLocation[] locations = null; 2039 if (libraries.length > 0) { 2040 locations = new LibraryLocation[libraries.length]; 2041 for (int j = 0; j < libraries.length; j++) { 2042 IConfigurationElement library = libraries[j]; 2043 String libPathStr = library.getAttribute("path"); //$NON-NLS-1$ 2044 if (libPathStr == null) { 2045 abort(NLS.bind("library for vmInstall {0} contributed by {1} missing required attribute libPath", //$NON-NLS-1$ 2046 new String[]{id, element.getContributor().getName()}), null); 2047 } 2048 String sourcePathStr = library.getAttribute("sourcePath"); //$NON-NLS-1$ 2049 String packageRootStr = library.getAttribute("packageRootPath"); //$NON-NLS-1$ 2050 String javadocOverride = library.getAttribute("javadocURL"); //$NON-NLS-1$ 2051 URL url = null; 2052 if (javadocOverride != null) { 2053 try { 2054 url = new URL(javadocOverride); 2055 } catch (MalformedURLException e) { 2056 abort(NLS.bind("Illegal javadocURL attribute specified for library {0} for vmInstall {1} contributed by {2}" //$NON-NLS-1$ 2057 ,new String[]{libPathStr, id, element.getContributor().getName()}), e); 2058 } 2059 } 2060 IPath homePath = new Path(home); 2061 IPath libPath = homePath.append(substitute(libPathStr)); 2062 IPath sourcePath = Path.EMPTY; 2063 if (sourcePathStr != null) { 2064 sourcePath = homePath.append(substitute(sourcePathStr)); 2065 } 2066 IPath packageRootPath = Path.EMPTY; 2067 if (packageRootStr != null) { 2068 packageRootPath = new Path(substitute(packageRootStr)); 2069 } 2070 locations[j] = new LibraryLocation(libPath, sourcePath, packageRootPath, url); 2071 } 2072 } 2073 standin.setLibraryLocations(locations); 2074 } 2075 // in case the contributed JRE attributes changed, remove it first, then add 2076 vmDefs.removeVM(standin); 2077 vmDefs.addVM(standin); 2078 } 2079 fgContributedVMs.add(id); 2080 } else { 2081 abort(NLS.bind("Illegal element {0} in vmInstalls extension contributed by {1}", //$NON-NLS-1$ 2082 new String[]{element.getName(), element.getContributor().getName()}), null); 2083 } 2084 } catch (CoreException e) { 2085 LaunchingPlugin.log(e); 2086 } 2087 } 2088 } 2089 2090 /** 2091 * Performs string substitution on the given expression. 2092 * 2093 * @param expression the expression to evaluate 2094 * @return expression after string substitution 2095 * @throws CoreException if the substitution encounters a problem 2096 * @since 3.2 2097 */ substitute(String expression)2098 private static String substitute(String expression) throws CoreException { 2099 return VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(expression); 2100 } 2101 2102 /** 2103 * Returns whether the VM install with the specified id was contributed via 2104 * the vmInstalls extension point. 2105 * 2106 * @param id VM id 2107 * @return whether the VM install was contributed via extension point 2108 * @since 3.2 2109 */ isContributedVMInstall(String id)2110 public static boolean isContributedVMInstall(String id) { 2111 getVMInstallTypes(); // ensure VMs are initialized 2112 return fgContributedVMs.contains(id); 2113 } 2114 2115 /** 2116 * Evaluates library locations for a IVMInstall. If no library locations are set on the install, a default 2117 * location is evaluated and checked if it exists. 2118 * @param vm the {@link IVMInstall} to compute locations for 2119 * @return library locations with paths that exist or are empty 2120 * @since 2.0 2121 */ getLibraryLocations(IVMInstall vm)2122 public static LibraryLocation[] getLibraryLocations(IVMInstall vm) { 2123 IPath[] libraryPaths; 2124 IPath[] sourcePaths; 2125 IPath[] sourceRootPaths; 2126 IPath[] annotationPaths; 2127 URL[] javadocLocations; 2128 URL[] indexes; 2129 LibraryLocation[] locations= vm.getLibraryLocations(); 2130 if (locations == null) { 2131 URL defJavaDocLocation = vm.getJavadocLocation(); 2132 File installLocation = vm.getInstallLocation(); 2133 if (installLocation == null) { 2134 return new LibraryLocation[0]; 2135 } 2136 LibraryLocation[] dflts= vm.getVMInstallType().getDefaultLibraryLocations(installLocation); 2137 libraryPaths = new IPath[dflts.length]; 2138 sourcePaths = new IPath[dflts.length]; 2139 sourceRootPaths = new IPath[dflts.length]; 2140 javadocLocations= new URL[dflts.length]; 2141 indexes = new URL[dflts.length]; 2142 annotationPaths = new IPath[dflts.length]; 2143 for (int i = 0; i < dflts.length; i++) { 2144 libraryPaths[i]= dflts[i].getSystemLibraryPath(); 2145 if (defJavaDocLocation == null) { 2146 javadocLocations[i]= dflts[i].getJavadocLocation(); 2147 } else { 2148 javadocLocations[i]= defJavaDocLocation; 2149 } 2150 indexes[i] = dflts[i].getIndexLocation(); 2151 if (!libraryPaths[i].toFile().isFile()) { 2152 libraryPaths[i]= Path.EMPTY; 2153 } 2154 2155 annotationPaths[i] = Path.EMPTY; 2156 2157 sourcePaths[i]= dflts[i].getSystemLibrarySourcePath(); 2158 if (sourcePaths[i].toFile().isFile()) { 2159 sourceRootPaths[i]= dflts[i].getPackageRootPath(); 2160 } else { 2161 sourcePaths[i]= Path.EMPTY; 2162 sourceRootPaths[i]= Path.EMPTY; 2163 } 2164 } 2165 } else { 2166 libraryPaths = new IPath[locations.length]; 2167 sourcePaths = new IPath[locations.length]; 2168 sourceRootPaths = new IPath[locations.length]; 2169 javadocLocations= new URL[locations.length]; 2170 indexes = new URL[locations.length]; 2171 annotationPaths = new IPath[locations.length]; 2172 for (int i = 0; i < locations.length; i++) { 2173 libraryPaths[i]= locations[i].getSystemLibraryPath(); 2174 sourcePaths[i]= locations[i].getSystemLibrarySourcePath(); 2175 sourceRootPaths[i]= locations[i].getPackageRootPath(); 2176 javadocLocations[i]= locations[i].getJavadocLocation(); 2177 annotationPaths[i] = locations[i].getExternalAnnotationsPath(); 2178 indexes[i] = locations[i].getIndexLocation(); 2179 } 2180 } 2181 locations = new LibraryLocation[sourcePaths.length]; 2182 for (int i = 0; i < sourcePaths.length; i++) { 2183 locations[i] = new LibraryLocation(libraryPaths[i], sourcePaths[i], sourceRootPaths[i], javadocLocations[i], indexes[i], annotationPaths[i]); 2184 } 2185 return locations; 2186 } 2187 2188 /** 2189 * Detect the VM that Eclipse is running on. 2190 * 2191 * @return a VM stand-in representing the VM that Eclipse is running on, or 2192 * <code>null</code> if unable to detect the runtime VM 2193 */ detectEclipseRuntime()2194 private static VMStandin detectEclipseRuntime() { 2195 // Try to detect a VM for each declared VM type 2196 IVMInstallType[] vmTypes= getVMInstallTypes(); 2197 // If we are running from an EE file, setup the VM from it 2198 for (int i = 0; i < vmTypes.length; i++) { 2199 if (vmTypes[i] instanceof EEVMType){ 2200 String eeFileName = System.getProperty("ee.filename"); //$NON-NLS-1$ 2201 if (eeFileName != null){ 2202 File vmFile = new File(eeFileName); 2203 if (vmFile.isDirectory()){ 2204 vmFile = new File(vmFile, "default.ee"); //$NON-NLS-1$ 2205 } 2206 if (vmFile.isFile()){ 2207 // Make sure the VM id is unique 2208 long unique = System.currentTimeMillis(); 2209 while (vmTypes[i].findVMInstall(String.valueOf(unique)) != null) { 2210 unique++; 2211 } 2212 2213 // Create a stand-in for the detected VM and add it to the result collector 2214 String vmID = String.valueOf(unique); 2215 try{ 2216 return createVMFromDefinitionFile(vmFile, "", vmID); //$NON-NLS-1$ 2217 } catch (CoreException e){ 2218 // The file was not a valid EE file, continue the detection process 2219 } 2220 } 2221 } 2222 } 2223 } 2224 2225 // Try to create a VM install using the install location 2226 for (int i = 0; i < vmTypes.length; i++) { 2227 File detectedLocation= vmTypes[i].detectInstallLocation(); 2228 if (detectedLocation != null) { 2229 2230 // Make sure the VM id is unique 2231 long unique = System.currentTimeMillis(); 2232 IVMInstallType vmType = vmTypes[i]; 2233 while (vmType.findVMInstall(String.valueOf(unique)) != null) { 2234 unique++; 2235 } 2236 2237 // Create a stand-in for the detected VM and add it to the result collector 2238 String vmID = String.valueOf(unique); 2239 VMStandin detectedVMStandin = new VMStandin(vmType, vmID); 2240 2241 // Java 9 and above needs the vmInstall location till jre 2242 File pluginDir = new File(detectedLocation, "plugins"); //$NON-NLS-1$ 2243 File featuresDir = new File(detectedLocation, "features"); //$NON-NLS-1$ 2244 if (pluginDir.exists() && featuresDir.exists()) { 2245 if (isJREVersionAbove8(vmType, detectedLocation)) { 2246 detectedLocation = new File(detectedLocation, "jre"); //$NON-NLS-1$ 2247 } 2248 } 2249 detectedVMStandin.setInstallLocation(detectedLocation); 2250 detectedVMStandin.setName(generateDetectedVMName(detectedVMStandin)); 2251 if (vmType instanceof AbstractVMInstallType) { 2252 AbstractVMInstallType abs = (AbstractVMInstallType)vmType; 2253 URL url = abs.getDefaultJavadocLocation(detectedLocation); 2254 detectedVMStandin.setJavadocLocation(url); 2255 String arguments = abs.getDefaultVMArguments(detectedLocation); 2256 if (arguments != null) { 2257 detectedVMStandin.setVMArgs(arguments); 2258 } 2259 } 2260 return detectedVMStandin; 2261 } 2262 } 2263 return null; 2264 } 2265 isJREVersionAbove8(IVMInstallType vmType, File installLocation)2266 private static boolean isJREVersionAbove8(IVMInstallType vmType, File installLocation) { 2267 LibraryLocation[] locations = vmType.getDefaultLibraryLocations(installLocation); 2268 boolean exist = true; 2269 for (int i = 0; i < locations.length; i++) { 2270 exist = exist && new File(locations[i].getSystemLibraryPath().toOSString()).exists(); 2271 } 2272 if (exist) { 2273 return false; 2274 } 2275 exist = true; 2276 LibraryLocation[] newLocations = vmType.getDefaultLibraryLocations(new File(installLocation, "jre")); //$NON-NLS-1$ 2277 for (int i = 0; i < newLocations.length; i++) { 2278 exist = exist && new File(newLocations[i].getSystemLibraryPath().toOSString()).exists(); 2279 } 2280 return exist; 2281 } 2282 2283 /** 2284 * Returns whether the specified option is the same in the given 2285 * map and preference store. 2286 * 2287 * <p> 2288 * Notes: 2289 * <ul> 2290 * <li>Returns <code>false</code> if the option is only contained in one map</li> 2291 * <li>Returns <code>true</code> if the option is not contained in either map</li> 2292 * </ul> 2293 * </p> 2294 * 2295 * @param optionName name of option to test 2296 * @param options map of options 2297 * @param prefStore preferences node 2298 * @return whether the options are the same in both maps 2299 */ equals(String optionName, Map<?, ?> options, org.osgi.service.prefs.Preferences prefStore)2300 private static boolean equals(String optionName, Map<?, ?> options, org.osgi.service.prefs.Preferences prefStore) { 2301 String dummy= new String(); 2302 String prefValue= prefStore.get(optionName, dummy); 2303 if (prefValue != null && prefValue != dummy) { 2304 return options.containsKey(optionName) && 2305 equals(prefValue, options.get(optionName)); 2306 } 2307 return !options.containsKey(optionName); 2308 } 2309 2310 /** 2311 * Returns whether the objects are equal or both <code>null</code> 2312 * 2313 * @param o1 an object 2314 * @param o2 an object 2315 * @return whether the objects are equal or both <code>null</code> 2316 */ equals(Object o1, Object o2)2317 private static boolean equals(Object o1, Object o2) { 2318 if (o1 == null) { 2319 return o2 == null; 2320 } 2321 return o1.equals(o2); 2322 } 2323 2324 /** 2325 * Make the name of a detected VM stand out. 2326 * @param vm the VM to generate a name for 2327 * @return the new name or <code>JRE</code> 2328 */ generateDetectedVMName(IVMInstall vm)2329 private static String generateDetectedVMName(IVMInstall vm) { 2330 String name = vm.getInstallLocation().getName(); 2331 name = name.trim(); 2332 if (name.length() == 0) { 2333 name = LaunchingMessages.JavaRuntime_25; 2334 } 2335 return name; 2336 } 2337 2338 /** 2339 * Creates and returns a classpath entry describing 2340 * the JRE_LIB classpath variable. 2341 * 2342 * @return a new IClasspathEntry that describes the JRE_LIB classpath variable 2343 */ getJREVariableEntry()2344 public static IClasspathEntry getJREVariableEntry() { 2345 return JavaCore.newVariableEntry( 2346 new Path(JRELIB_VARIABLE), 2347 new Path(JRESRC_VARIABLE), 2348 new Path(JRESRCROOT_VARIABLE) 2349 ); 2350 } 2351 2352 /** 2353 * Creates and returns a classpath entry describing 2354 * the default JRE container entry. 2355 * 2356 * @return a new IClasspathEntry that describes the default JRE container entry 2357 * @since 2.0 2358 */ getDefaultJREContainerEntry()2359 public static IClasspathEntry getDefaultJREContainerEntry() { 2360 return JavaCore.newContainerEntry(newDefaultJREContainerPath()); 2361 } 2362 2363 /** 2364 * Returns a path for the JRE classpath container identifying the 2365 * default VM install. 2366 * 2367 * @return classpath container path 2368 * @since 3.2 2369 */ newDefaultJREContainerPath()2370 public static IPath newDefaultJREContainerPath() { 2371 return new Path(JRE_CONTAINER); 2372 } 2373 2374 /** 2375 * Returns a path for the JRE classpath container identifying the 2376 * specified VM install by type and name. 2377 * 2378 * @param vm VM install 2379 * @return classpath container path 2380 * @since 3.2 2381 */ newJREContainerPath(IVMInstall vm)2382 public static IPath newJREContainerPath(IVMInstall vm) { 2383 return newJREContainerPath(vm.getVMInstallType().getId(), vm.getName()); 2384 } 2385 2386 /** 2387 * Returns a path for the JRE classpath container identifying the 2388 * specified VM install by type and name. 2389 * 2390 * @param typeId VM install type identifier 2391 * @param name VM install name 2392 * @return classpath container path 2393 * @since 3.2 2394 */ newJREContainerPath(String typeId, String name)2395 public static IPath newJREContainerPath(String typeId, String name) { 2396 IPath path = newDefaultJREContainerPath(); 2397 path = path.append(typeId); 2398 path = path.append(name); 2399 return path; 2400 } 2401 2402 /** 2403 * Returns a path for the JRE classpath container identifying the 2404 * specified execution environment. 2405 * 2406 * @param environment execution environment 2407 * @return classpath container path 2408 * @since 3.2 2409 */ newJREContainerPath(IExecutionEnvironment environment)2410 public static IPath newJREContainerPath(IExecutionEnvironment environment) { 2411 IPath path = newDefaultJREContainerPath(); 2412 path = path.append(StandardVMType.ID_STANDARD_VM_TYPE); 2413 path = path.append(JREContainerInitializer.encodeEnvironmentId(environment.getId())); 2414 return path; 2415 } 2416 2417 /** 2418 * Returns the JRE referenced by the specified JRE classpath container 2419 * path or <code>null</code> if none. 2420 * 2421 * @param jreContainerPath the path to the container to try and resolve the {@link IVMInstall} from 2422 * @return JRE referenced by the specified JRE classpath container 2423 * path or <code>null</code> 2424 * @since 3.2 2425 */ getVMInstall(IPath jreContainerPath)2426 public static IVMInstall getVMInstall(IPath jreContainerPath) { 2427 return JREContainerInitializer.resolveVM(jreContainerPath); 2428 } 2429 2430 /** 2431 * Returns the identifier of the VM install type referenced by the 2432 * given JRE classpath container path, or <code>null</code> if none. 2433 * 2434 * @param jreContainerPath the path to the container to try and resolve the {@link IVMInstallType} id from 2435 * @return VM install type identifier or <code>null</code> 2436 * @since 3.2 2437 */ getVMInstallTypeId(IPath jreContainerPath)2438 public static String getVMInstallTypeId(IPath jreContainerPath) { 2439 if (JREContainerInitializer.isExecutionEnvironment(jreContainerPath)) { 2440 return null; 2441 } 2442 return JREContainerInitializer.getVMTypeId(jreContainerPath); 2443 } 2444 2445 /** 2446 * Returns the name of the VM install referenced by the 2447 * given JRE classpath container path, or <code>null</code> if none. 2448 * 2449 * @param jreContainerPath the path to the container to try an resolve the {@link IVMInstall} name from 2450 * @return VM name or <code>null</code> 2451 * @since 3.2 2452 */ getVMInstallName(IPath jreContainerPath)2453 public static String getVMInstallName(IPath jreContainerPath) { 2454 if (JREContainerInitializer.isExecutionEnvironment(jreContainerPath)) { 2455 return null; 2456 } 2457 return JREContainerInitializer.getVMName(jreContainerPath); 2458 } 2459 2460 /** 2461 * Returns the execution environment identifier in the following JRE 2462 * classpath container path, or <code>null</code> if none. 2463 * 2464 * @param jreContainerPath classpath container path 2465 * @return execution environment identifier or <code>null</code> 2466 * @since 3.2 2467 */ getExecutionEnvironmentId(IPath jreContainerPath)2468 public static String getExecutionEnvironmentId(IPath jreContainerPath) { 2469 return JREContainerInitializer.getExecutionEnvironmentId(jreContainerPath); 2470 } 2471 2472 /** 2473 * Returns a runtime classpath entry identifying the JRE to use when launching the specified 2474 * configuration or <code>null</code> if none is specified. The entry returned represents a 2475 * either a classpath variable or classpath container that resolves to a JRE. 2476 * <p> 2477 * The entry is resolved as follows: 2478 * <ol> 2479 * <li>If the <code>ATTR_JRE_CONTAINER_PATH</code> is present, it is used to create 2480 * a classpath container referring to a JRE.</li> 2481 * <li>Next, if the <code>ATTR_VM_INSTALL_TYPE</code> and <code>ATTR_VM_INSTALL_NAME</code> 2482 * attributes are present, they are used to create a classpath container.</li> 2483 * <li>When none of the above attributes are specified, a default entry is 2484 * created which refers to the JRE referenced by the build path of the configuration's 2485 * associated Java project. This could be a classpath variable or classpath container.</li> 2486 * <li>When there is no Java project associated with a configuration, the workspace 2487 * default JRE is used to create a container path.</li> 2488 * </ol> 2489 * @param configuration the backing {@link ILaunchConfiguration} 2490 * @return classpath container path identifying a JRE or <code>null</code> 2491 * @exception org.eclipse.core.runtime.CoreException if an exception occurs retrieving 2492 * attributes from the specified launch configuration 2493 * @since 3.2 2494 */ computeJREEntry(ILaunchConfiguration configuration)2495 public static IRuntimeClasspathEntry computeJREEntry(ILaunchConfiguration configuration) throws CoreException { 2496 String jreAttr = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, (String)null); 2497 IPath containerPath = null; 2498 if (jreAttr == null) { 2499 @SuppressWarnings("deprecation") 2500 String type = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_TYPE, (String)null); 2501 if (type == null) { 2502 // default JRE for the launch configuration 2503 IJavaProject proj = getJavaProject(configuration); 2504 if (proj == null) { 2505 containerPath = newDefaultJREContainerPath(); 2506 } else { 2507 if (isModularConfiguration(configuration)) { 2508 return computeModularJREEntry(proj); 2509 } 2510 return computeJREEntry(proj); 2511 } 2512 } else { 2513 @SuppressWarnings("deprecation") 2514 String name = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_INSTALL_NAME, (String)null); 2515 if (name != null) { 2516 containerPath = newDefaultJREContainerPath().append(type).append(name); 2517 } 2518 } 2519 } else { 2520 containerPath = Path.fromPortableString(jreAttr); 2521 } 2522 if (containerPath != null) { 2523 if (isModularConfiguration(configuration)) { 2524 return newRuntimeContainerClasspathEntry(containerPath, IRuntimeClasspathEntry.MODULE_PATH); 2525 } 2526 return newRuntimeContainerClasspathEntry(containerPath, IRuntimeClasspathEntry.STANDARD_CLASSES); 2527 } 2528 return null; 2529 } 2530 2531 /** 2532 * Returns a runtime classpath entry identifying the JRE referenced by the specified 2533 * project, or <code>null</code> if none. The entry returned represents a either a 2534 * classpath variable or classpath container that resolves to a JRE. 2535 * 2536 * @param project Java project 2537 * @return JRE runtime classpath entry or <code>null</code> 2538 * @exception org.eclipse.core.runtime.CoreException if an exception occurs 2539 * accessing the project's classpath 2540 * @since 3.2 2541 */ computeJREEntry(IJavaProject project)2542 public static IRuntimeClasspathEntry computeJREEntry(IJavaProject project) throws CoreException { 2543 IClasspathEntry[] rawClasspath = project.getRawClasspath(); 2544 IRuntimeClasspathEntryResolver2 resolver = null; 2545 for (int i = 0; i < rawClasspath.length; i++) { 2546 IClasspathEntry entry = rawClasspath[i]; 2547 switch (entry.getEntryKind()) { 2548 case IClasspathEntry.CPE_VARIABLE: 2549 resolver = getVariableResolver(entry.getPath().segment(0)); 2550 if (resolver != null) { 2551 if (resolver.isVMInstallReference(entry)) { 2552 return newRuntimeClasspathEntry(entry); 2553 } 2554 } 2555 break; 2556 case IClasspathEntry.CPE_CONTAINER: 2557 resolver = getContainerResolver(entry.getPath().segment(0)); 2558 if (resolver != null) { 2559 if (resolver.isVMInstallReference(entry)) { 2560 IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), project); 2561 if (container != null) { 2562 switch (container.getKind()) { 2563 case IClasspathContainer.K_APPLICATION: 2564 break; 2565 case IClasspathContainer.K_DEFAULT_SYSTEM: 2566 return newRuntimeContainerClasspathEntry(entry.getPath(), IRuntimeClasspathEntry.STANDARD_CLASSES); 2567 case IClasspathContainer.K_SYSTEM: 2568 return newRuntimeContainerClasspathEntry(entry.getPath(), IRuntimeClasspathEntry.BOOTSTRAP_CLASSES); 2569 } 2570 } 2571 } 2572 } 2573 break; 2574 } 2575 2576 } 2577 return null; 2578 } 2579 2580 /** 2581 * Returns a runtime classpath or modulepath entry identifying the JRE referenced by the specified project, or <code>null</code> if none. The 2582 * entry returned represents a either a classpath variable or classpath container that resolves to a JRE. 2583 * 2584 * @param project 2585 * Java project 2586 * @return JRE runtime classpath or modulepath entry or <code>null</code> 2587 * @exception org.eclipse.core.runtime.CoreException 2588 * if an exception occurs accessing the project's classpath 2589 * @since 3.10 2590 */ computeModularJREEntry(IJavaProject project)2591 public static IRuntimeClasspathEntry computeModularJREEntry(IJavaProject project) throws CoreException { 2592 IClasspathEntry[] rawClasspath = project.getRawClasspath(); 2593 IRuntimeClasspathEntryResolver2 resolver = null; 2594 for (int i = 0; i < rawClasspath.length; i++) { 2595 IClasspathEntry entry = rawClasspath[i]; 2596 switch (entry.getEntryKind()) { 2597 case IClasspathEntry.CPE_VARIABLE: 2598 resolver = getVariableResolver(entry.getPath().segment(0)); 2599 if (resolver != null) { 2600 if (resolver.isVMInstallReference(entry)) { 2601 if (isModularProject(project)) { 2602 return newRuntimeClasspathEntry(entry, IRuntimeClasspathEntry.MODULE_PATH); 2603 } 2604 return newRuntimeClasspathEntry(entry, IRuntimeClasspathEntry.CLASS_PATH); 2605 } 2606 } 2607 break; 2608 case IClasspathEntry.CPE_CONTAINER: 2609 resolver = getContainerResolver(entry.getPath().segment(0)); 2610 if (resolver != null) { 2611 if (resolver.isVMInstallReference(entry)) { 2612 IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), project); 2613 if (container != null) { 2614 switch (container.getKind()) { 2615 case IClasspathContainer.K_APPLICATION: 2616 break; 2617 case IClasspathContainer.K_DEFAULT_SYSTEM: 2618 if (isModularProject(project)) { 2619 return newRuntimeContainerClasspathEntry(entry.getPath(), IRuntimeClasspathEntry.MODULE_PATH); 2620 } 2621 return newRuntimeContainerClasspathEntry(entry.getPath(), IRuntimeClasspathEntry.CLASS_PATH); 2622 case IClasspathContainer.K_SYSTEM: 2623 if (isModularProject(project)) { 2624 return newRuntimeContainerClasspathEntry(entry.getPath(), IRuntimeClasspathEntry.MODULE_PATH); 2625 } 2626 return newRuntimeContainerClasspathEntry(entry.getPath(), IRuntimeClasspathEntry.CLASS_PATH); 2627 } 2628 } 2629 } 2630 } 2631 break; 2632 } 2633 2634 } 2635 return null; 2636 } 2637 2638 /** 2639 * Returns whether the given runtime classpath entry refers to a VM install. 2640 * 2641 * @param entry the entry to check 2642 * @return whether the given runtime classpath entry refers to a VM install 2643 * @since 3.2 2644 */ isVMInstallReference(IRuntimeClasspathEntry entry)2645 public static boolean isVMInstallReference(IRuntimeClasspathEntry entry) { 2646 IClasspathEntry classpathEntry = entry.getClasspathEntry(); 2647 if (classpathEntry != null) { 2648 switch (classpathEntry.getEntryKind()) { 2649 case IClasspathEntry.CPE_VARIABLE: 2650 IRuntimeClasspathEntryResolver2 resolver = getVariableResolver(classpathEntry.getPath().segment(0)); 2651 if (resolver != null) { 2652 return resolver.isVMInstallReference(classpathEntry); 2653 } 2654 break; 2655 case IClasspathEntry.CPE_CONTAINER: 2656 resolver = getContainerResolver(classpathEntry.getPath().segment(0)); 2657 if (resolver != null) { 2658 return resolver.isVMInstallReference(classpathEntry); 2659 } 2660 break; 2661 } 2662 } 2663 return false; 2664 } 2665 2666 /** 2667 * Returns the VM connector defined with the specified identifier, 2668 * or <code>null</code> if none. 2669 * 2670 * @param id VM connector identifier 2671 * @return VM connector or <code>null</code> if none 2672 * @since 2.0 2673 */ getVMConnector(String id)2674 public static IVMConnector getVMConnector(String id) { 2675 return LaunchingPlugin.getDefault().getVMConnector(id); 2676 } 2677 2678 /** 2679 * Returns all VM connector extensions. 2680 * 2681 * @return VM connectors 2682 * @since 2.0 2683 */ getVMConnectors()2684 public static IVMConnector[] getVMConnectors() { 2685 return LaunchingPlugin.getDefault().getVMConnectors(); 2686 } 2687 2688 /** 2689 * Returns the preference store for the launching plug-in. 2690 * 2691 * @return the preference store for the launching plug-in 2692 * @since 2.0 2693 */ 2694 @SuppressWarnings("deprecation") getPreferences()2695 public static Preferences getPreferences() { 2696 return LaunchingPlugin.getDefault().getPluginPreferences(); 2697 } 2698 2699 /** 2700 * Saves the preferences for the launching plug-in. 2701 * 2702 * @since 2.0 2703 */ savePreferences()2704 public static void savePreferences() { 2705 IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN); 2706 try { 2707 prefs.flush(); 2708 } catch (BackingStoreException e) { 2709 LaunchingPlugin.log(e); 2710 } 2711 } 2712 2713 /** 2714 * Registers the given resolver for the specified variable. 2715 * 2716 * @param resolver runtime classpath entry resolver 2717 * @param variableName variable name to register for 2718 * @since 2.0 2719 */ addVariableResolver(IRuntimeClasspathEntryResolver resolver, String variableName)2720 public static void addVariableResolver(IRuntimeClasspathEntryResolver resolver, String variableName) { 2721 Map<String, IRuntimeClasspathEntryResolver> map = getVariableResolvers(); 2722 map.put(variableName, resolver); 2723 } 2724 2725 /** 2726 * Registers the given resolver for the specified container. 2727 * 2728 * @param resolver runtime classpath entry resolver 2729 * @param containerIdentifier identifier of the classpath container to register for 2730 * @since 2.0 2731 */ addContainerResolver(IRuntimeClasspathEntryResolver resolver, String containerIdentifier)2732 public static void addContainerResolver(IRuntimeClasspathEntryResolver resolver, String containerIdentifier) { 2733 Map<String, IRuntimeClasspathEntryResolver> map = getContainerResolvers(); 2734 map.put(containerIdentifier, resolver); 2735 } 2736 2737 /** 2738 * Returns all registered variable resolvers. 2739 * @return the initialized map of {@link RuntimeClasspathEntryResolver}s for variables 2740 */ getVariableResolvers()2741 private static Map<String, IRuntimeClasspathEntryResolver> getVariableResolvers() { 2742 if (fgVariableResolvers == null) { 2743 initializeResolvers(); 2744 } 2745 return fgVariableResolvers; 2746 } 2747 2748 /** 2749 * Returns all registered container resolvers. 2750 * @return the initialized map of {@link RuntimeClasspathEntryResolver}s for containers 2751 */ getContainerResolvers()2752 private static Map<String, IRuntimeClasspathEntryResolver> getContainerResolvers() { 2753 if (fgContainerResolvers == null) { 2754 initializeResolvers(); 2755 } 2756 return fgContainerResolvers; 2757 } 2758 2759 /** 2760 * Returns all registered runtime classpath entry resolvers. 2761 * @return the initialized map of {@link RuntimeClasspathEntryResolver}s for classpath entries 2762 */ getEntryResolvers()2763 private static Map<String, RuntimeClasspathEntryResolver> getEntryResolvers() { 2764 if (fgRuntimeClasspathEntryResolvers == null) { 2765 initializeResolvers(); 2766 } 2767 return fgRuntimeClasspathEntryResolvers; 2768 } 2769 2770 /** 2771 * Initializes the listing of runtime classpath entry resolvers 2772 */ initializeResolvers()2773 private static void initializeResolvers() { 2774 IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(LaunchingPlugin.ID_PLUGIN, EXTENSION_POINT_RUNTIME_CLASSPATH_ENTRY_RESOLVERS); 2775 IConfigurationElement[] extensions = point.getConfigurationElements(); 2776 fgVariableResolvers = new HashMap<>(extensions.length); 2777 fgContainerResolvers = new HashMap<>(extensions.length); 2778 fgRuntimeClasspathEntryResolvers = new HashMap<>(extensions.length); 2779 for (int i = 0; i < extensions.length; i++) { 2780 RuntimeClasspathEntryResolver res = new RuntimeClasspathEntryResolver(extensions[i]); 2781 String variable = res.getVariableName(); 2782 String container = res.getContainerId(); 2783 String entryId = res.getRuntimeClasspathEntryId(); 2784 if (variable != null) { 2785 fgVariableResolvers.put(variable, res); 2786 } 2787 if (container != null) { 2788 fgContainerResolvers.put(container, res); 2789 } 2790 if (entryId != null) { 2791 fgRuntimeClasspathEntryResolvers.put(entryId, res); 2792 } 2793 } 2794 } 2795 2796 /** 2797 * Returns all registered classpath providers. 2798 * @return the initialized map of {@link RuntimeClasspathProvider}s 2799 */ getClasspathProviders()2800 private static Map<String, RuntimeClasspathProvider> getClasspathProviders() { 2801 if (fgPathProviders == null) { 2802 initializeProviders(); 2803 } 2804 return fgPathProviders; 2805 } 2806 2807 /** 2808 * Initializes the listing of classpath providers 2809 */ initializeProviders()2810 private static void initializeProviders() { 2811 IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(LaunchingPlugin.ID_PLUGIN, EXTENSION_POINT_RUNTIME_CLASSPATH_PROVIDERS); 2812 IConfigurationElement[] extensions = point.getConfigurationElements(); 2813 fgPathProviders = new HashMap<>(extensions.length); 2814 for (int i = 0; i < extensions.length; i++) { 2815 RuntimeClasspathProvider res = new RuntimeClasspathProvider(extensions[i]); 2816 fgPathProviders.put(res.getIdentifier(), res); 2817 } 2818 } 2819 2820 /** 2821 * Returns the resolver registered for the given variable, or 2822 * <code>null</code> if none. 2823 * 2824 * @param variableName the variable to determine the resolver for 2825 * @return the resolver registered for the given variable, or 2826 * <code>null</code> if none 2827 */ getVariableResolver(String variableName)2828 private static IRuntimeClasspathEntryResolver2 getVariableResolver(String variableName) { 2829 return (IRuntimeClasspathEntryResolver2)getVariableResolvers().get(variableName); 2830 } 2831 2832 /** 2833 * Returns the resolver registered for the given container id, or 2834 * <code>null</code> if none. 2835 * 2836 * @param containerId the container to determine the resolver for 2837 * @return the resolver registered for the given container id, or 2838 * <code>null</code> if none 2839 */ getContainerResolver(String containerId)2840 private static IRuntimeClasspathEntryResolver2 getContainerResolver(String containerId) { 2841 return (IRuntimeClasspathEntryResolver2)getContainerResolvers().get(containerId); 2842 } 2843 2844 /** 2845 * Returns the resolver registered for the given contributed classpath 2846 * entry type. 2847 * 2848 * @param typeId the id of the contributed classpath entry 2849 * @return the resolver registered for the given classpath entry 2850 */ getContributedResolver(String typeId)2851 private static IRuntimeClasspathEntryResolver getContributedResolver(String typeId) { 2852 IRuntimeClasspathEntryResolver resolver = getEntryResolvers().get(typeId); 2853 if (resolver == null) { 2854 return new DefaultEntryResolver(); 2855 } 2856 return resolver; 2857 } 2858 2859 /** 2860 * Adds the given listener to the list of registered VM install changed 2861 * listeners. Has no effect if an identical listener is already registered. 2862 * 2863 * @param listener the listener to add 2864 * @since 2.0 2865 */ addVMInstallChangedListener(IVMInstallChangedListener listener)2866 public static void addVMInstallChangedListener(IVMInstallChangedListener listener) { 2867 fgVMListeners.add(listener); 2868 } 2869 2870 /** 2871 * Removes the given listener from the list of registered VM install changed 2872 * listeners. Has no effect if an identical listener is not already registered. 2873 * 2874 * @param listener the listener to remove 2875 * @since 2.0 2876 */ removeVMInstallChangedListener(IVMInstallChangedListener listener)2877 public static void removeVMInstallChangedListener(IVMInstallChangedListener listener) { 2878 fgVMListeners.remove(listener); 2879 } 2880 2881 /** 2882 * Notifies registered listeners that the default VM has changed 2883 * @param previous the previous VM 2884 * @param current the new current default VM 2885 */ notifyDefaultVMChanged(IVMInstall previous, IVMInstall current)2886 private static void notifyDefaultVMChanged(IVMInstall previous, IVMInstall current) { 2887 for (IVMInstallChangedListener listener : fgVMListeners) { 2888 listener.defaultVMInstallChanged(previous, current); 2889 } 2890 } 2891 2892 /** 2893 * Notifies all VM install changed listeners of the given property change. 2894 * 2895 * @param event event describing the change. 2896 * @since 2.0 2897 */ fireVMChanged(PropertyChangeEvent event)2898 public static void fireVMChanged(PropertyChangeEvent event) { 2899 for (IVMInstallChangedListener listener : fgVMListeners) { 2900 listener.vmChanged(event); 2901 } 2902 } 2903 2904 /** 2905 * Notifies all VM install changed listeners of the VM addition 2906 * 2907 * @param vm the VM that has been added 2908 * @since 2.0 2909 */ fireVMAdded(IVMInstall vm)2910 public static void fireVMAdded(IVMInstall vm) { 2911 if (!fgInitializingVMs) { 2912 for (IVMInstallChangedListener listener : fgVMListeners) { 2913 listener.vmAdded(vm); 2914 } 2915 } 2916 } 2917 2918 /** 2919 * Notifies all VM install changed listeners of the VM removal 2920 * 2921 * @param vm the VM that has been removed 2922 * @since 2.0 2923 */ fireVMRemoved(IVMInstall vm)2924 public static void fireVMRemoved(IVMInstall vm) { 2925 for (IVMInstallChangedListener listener : fgVMListeners) { 2926 listener.vmRemoved(vm); 2927 } 2928 } 2929 2930 /** 2931 * Return the String representation of the default output directory of the 2932 * launch config's project or <code>null</code> if there is no configuration, no 2933 * project or some sort of problem. 2934 * @param config the {@link ILaunchConfiguration} 2935 * 2936 * @return the default output directory for the specified launch 2937 * configuration's project 2938 * @since 2.1 2939 */ getProjectOutputDirectory(ILaunchConfiguration config)2940 public static String getProjectOutputDirectory(ILaunchConfiguration config) { 2941 try { 2942 if (config != null) { 2943 IJavaProject javaProject = JavaRuntime.getJavaProject(config); 2944 if (javaProject != null) { 2945 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); 2946 IPath outputLocation = javaProject.getOutputLocation(); 2947 IResource resource = root.findMember(outputLocation); 2948 if (resource != null) { 2949 IPath path = resource.getFullPath(); 2950 if (path != null) { 2951 return path.makeRelative().toString(); 2952 } 2953 } 2954 } 2955 } 2956 } catch (CoreException ce) { 2957 } 2958 return null; 2959 } 2960 2961 /** 2962 * Returns a collection of source containers corresponding to the given 2963 * resolved runtime classpath entries. 2964 * <p> 2965 * Note that the entries must be resolved to ARCHIVE and PROJECT entries, 2966 * as source containers cannot be determined for unresolved entries. 2967 * </p> 2968 * @param entries entries to translate 2969 * @return source containers corresponding to the given runtime classpath entries 2970 * @since 3.1 2971 */ getSourceContainers(IRuntimeClasspathEntry[] entries)2972 public static ISourceContainer[] getSourceContainers(IRuntimeClasspathEntry[] entries) { 2973 return JavaSourceLookupUtil.translate(entries); 2974 } 2975 2976 /** 2977 * Returns a collection of paths that should be appended to the given project's 2978 * <code>java.library.path</code> system property when launched. Entries are 2979 * searched for on the project's build path as extra classpath attributes. 2980 * Each entry represents an absolute path in the local file system. 2981 * 2982 * @param project the project to compute the <code>java.library.path</code> for 2983 * @param requiredProjects whether to consider entries in required projects 2984 * @return a collection of paths representing entries that should be appended 2985 * to the given project's <code>java.library.path</code> 2986 * @throws CoreException if unable to compute the Java library path 2987 * @since 3.1 2988 * @see org.eclipse.jdt.core.IClasspathAttribute 2989 * @see JavaRuntime#CLASSPATH_ATTR_LIBRARY_PATH_ENTRY 2990 */ computeJavaLibraryPath(IJavaProject project, boolean requiredProjects)2991 public static String[] computeJavaLibraryPath(IJavaProject project, boolean requiredProjects) throws CoreException { 2992 Set<IJavaProject> visited = new HashSet<>(); 2993 List<String> entries = new ArrayList<>(); 2994 gatherJavaLibraryPathEntries(project, requiredProjects, visited, entries); 2995 List<String> resolved = new ArrayList<>(entries.size()); 2996 Iterator<String> iterator = entries.iterator(); 2997 IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); 2998 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); 2999 while (iterator.hasNext()) { 3000 String entry = iterator.next(); 3001 String resolvedEntry = manager.performStringSubstitution(entry); 3002 IPath path = new Path(resolvedEntry); 3003 if (path.isAbsolute()) { 3004 File file = path.toFile(); 3005 resolved.add(file.getAbsolutePath()); 3006 } else { 3007 IResource resource = root.findMember(path); 3008 if (resource != null) { 3009 IPath location = resource.getLocation(); 3010 if (location != null) { 3011 resolved.add(location.toFile().getAbsolutePath()); 3012 } 3013 } 3014 } 3015 } 3016 return resolved.toArray(new String[resolved.size()]); 3017 } 3018 3019 /** 3020 * Gathers all Java library entries for the given project and optionally its required 3021 * projects. 3022 * 3023 * @param project project to gather entries for 3024 * @param requiredProjects whether to consider required projects 3025 * @param visited projects already considered 3026 * @param entries collection to add library entries to 3027 * @throws CoreException if unable to gather classpath entries 3028 * @since 3.1 3029 */ gatherJavaLibraryPathEntries(IJavaProject project, boolean requiredProjects, Set<IJavaProject> visited, List<String> entries)3030 private static void gatherJavaLibraryPathEntries(IJavaProject project, boolean requiredProjects, Set<IJavaProject> visited, List<String> entries) throws CoreException { 3031 if (visited.contains(project)) { 3032 return; 3033 } 3034 visited.add(project); 3035 IClasspathEntry[] rawClasspath = project.getRawClasspath(); 3036 IClasspathEntry[] required = processJavaLibraryPathEntries(project, requiredProjects, rawClasspath, entries); 3037 if (required != null) { 3038 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); 3039 for (int i = 0; i < required.length; i++) { 3040 IClasspathEntry entry = required[i]; 3041 String projectName = entry.getPath().segment(0); 3042 IProject p = root.getProject(projectName); 3043 if (p.exists()) { 3044 IJavaProject requiredProject = JavaCore.create(p); 3045 if(requiredProject.isOpen()) { 3046 gatherJavaLibraryPathEntries(requiredProject, requiredProjects, visited, entries); 3047 } 3048 else if(!isOptional(entry)) { 3049 gatherJavaLibraryPathEntries(requiredProject, requiredProjects, visited, entries); 3050 } 3051 } 3052 } 3053 } 3054 } 3055 3056 /** 3057 * Adds all java library path extra classpath entry values to the given entries collection 3058 * specified on the given project's classpath, and returns a collection of required 3059 * projects, or <code>null</code>. 3060 * 3061 * @param project project being processed 3062 * @param collectRequired whether to collect required projects 3063 * @param classpathEntries the project's raw classpath 3064 * @param entries collection to add java library path entries to 3065 * @return required project classpath entries or <code>null</code> 3066 * @throws CoreException if an exception occurs 3067 * @since 3.1 3068 */ processJavaLibraryPathEntries(IJavaProject project, boolean collectRequired, IClasspathEntry[] classpathEntries, List<String> entries)3069 private static IClasspathEntry[] processJavaLibraryPathEntries(IJavaProject project, boolean collectRequired, IClasspathEntry[] classpathEntries, List<String> entries) throws CoreException { 3070 List<IClasspathEntry> req = null; 3071 for (int i = 0; i < classpathEntries.length; i++) { 3072 IClasspathEntry entry = classpathEntries[i]; 3073 IClasspathAttribute[] extraAttributes = entry.getExtraAttributes(); 3074 for (int j = 0; j < extraAttributes.length; j++) { 3075 String[] paths = getLibraryPaths(extraAttributes[j]); 3076 if (paths != null) { 3077 for (int k = 0; k < paths.length; k++) { 3078 entries.add(paths[k]); 3079 } 3080 } 3081 } 3082 if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { 3083 IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), project); 3084 if (container != null) { 3085 IClasspathEntry[] requiredProjects = processJavaLibraryPathEntries(project, collectRequired, container.getClasspathEntries(), entries); 3086 if (requiredProjects != null) { 3087 if (req == null) { 3088 req = new ArrayList<>(); 3089 } 3090 for (int j = 0; j < requiredProjects.length; j++) { 3091 req.add(requiredProjects[j]); 3092 } 3093 } 3094 } 3095 } else if (collectRequired && entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { 3096 if (req == null) { 3097 req = new ArrayList<>(); 3098 } 3099 req.add(entry); 3100 } 3101 } 3102 if (req != null) { 3103 return req.toArray(new IClasspathEntry[req.size()]); 3104 } 3105 return null; 3106 } 3107 3108 /** 3109 * Creates a new classpath attribute referencing a list of shared libraries that should 3110 * appear on the <code>-Djava.library.path</code> system property at runtime 3111 * for an associated {@link IClasspathEntry}. 3112 * <p> 3113 * The factory methods <code>newLibraryPathsAttribute(String[])</code> 3114 * and <code>getLibraryPaths(IClasspathAttribute)</code> should be used to 3115 * encode and decode the attribute value. 3116 * </p> 3117 * @param paths an array of strings representing paths of shared libraries. 3118 * Each string is used to create an <code>IPath</code> using the constructor 3119 * <code>Path(String)</code>, and may contain <code>IStringVariable</code>'s. 3120 * Variable substitution is performed on each string before a path is constructed 3121 * from a string. 3122 * @return a classpath attribute with the name <code>CLASSPATH_ATTR_LIBRARY_PATH_ENTRY</code> 3123 * and an value encoded to the specified paths. 3124 * @since 3.1 3125 */ newLibraryPathsAttribute(String[] paths)3126 public static IClasspathAttribute newLibraryPathsAttribute(String[] paths) { 3127 StringBuilder value = new StringBuilder(); 3128 for (int i = 0; i < paths.length; i++) { 3129 value.append(paths[i]); 3130 if (i < (paths.length - 1)) { 3131 value.append("|"); //$NON-NLS-1$ 3132 } 3133 } 3134 return JavaCore.newClasspathAttribute(CLASSPATH_ATTR_LIBRARY_PATH_ENTRY, value.toString()); 3135 } 3136 3137 /** 3138 * Returns an array of strings referencing shared libraries that should 3139 * appear on the <code>-Djava.library.path</code> system property at runtime 3140 * for an associated {@link IClasspathEntry}, or <code>null</code> if the 3141 * given attribute is not a <code>CLASSPATH_ATTR_LIBRARY_PATH_ENTRY</code>. 3142 * Each string is used to create an <code>IPath</code> using the constructor 3143 * <code>Path(String)</code>, and may contain <code>IStringVariable</code>'s. 3144 * <p> 3145 * The factory methods <code>newLibraryPathsAttribute(String[])</code> 3146 * and <code>getLibraryPaths(IClasspathAttribute)</code> should be used to 3147 * encode and decode the attribute value. 3148 * </p> 3149 * @param attribute a <code>CLASSPATH_ATTR_LIBRARY_PATH_ENTRY</code> classpath attribute 3150 * @return an array of strings referencing shared libraries that should 3151 * appear on the <code>-Djava.library.path</code> system property at runtime 3152 * for an associated {@link IClasspathEntry}, or <code>null</code> if the 3153 * given attribute is not a <code>CLASSPATH_ATTR_LIBRARY_PATH_ENTRY</code>. 3154 * Each string is used to create an <code>IPath</code> using the constructor 3155 * <code>Path(String)</code>, and may contain <code>IStringVariable</code>'s. 3156 * @since 3.1 3157 */ getLibraryPaths(IClasspathAttribute attribute)3158 public static String[] getLibraryPaths(IClasspathAttribute attribute) { 3159 if (CLASSPATH_ATTR_LIBRARY_PATH_ENTRY.equals(attribute.getName())) { 3160 String value = attribute.getValue(); 3161 return value.split("\\|"); //$NON-NLS-1$ 3162 } 3163 return null; 3164 } 3165 3166 /** 3167 * Returns the execution environments manager. 3168 * 3169 * @return execution environments manager 3170 * @since 3.2 3171 */ getExecutionEnvironmentsManager()3172 public static IExecutionEnvironmentsManager getExecutionEnvironmentsManager() { 3173 return EnvironmentsManager.getDefault(); 3174 } 3175 3176 /** 3177 * Perform VM type and VM install initialization. Does not hold locks 3178 * while performing change notification. 3179 * 3180 * @since 3.2 3181 */ initializeVMs()3182 private static void initializeVMs() { 3183 VMDefinitionsContainer vmDefs = null; 3184 boolean setPref = false; 3185 boolean updateCompliance = false; 3186 synchronized (fgVMLock) { 3187 if (fgVMTypes == null) { 3188 try { 3189 fgInitializingVMs = true; 3190 // 1. load VM type extensions 3191 initializeVMTypeExtensions(); 3192 try { 3193 vmDefs = new VMDefinitionsContainer(); 3194 // 2. add persisted VMs 3195 setPref = addPersistedVMs(vmDefs); 3196 IStatus status = vmDefs.getStatus(); 3197 if (status != null) { 3198 if (status.isMultiStatus()) { 3199 MultiStatus multi = (MultiStatus) status; 3200 IStatus[] children = multi.getChildren(); 3201 for (int i = 0; i < children.length; i++) { 3202 IStatus child = children[i]; 3203 if (!child.isOK()) { 3204 LaunchingPlugin.log(child); 3205 } 3206 } 3207 } else if (!status.isOK()) { 3208 LaunchingPlugin.log(status); 3209 } 3210 } 3211 3212 // 3. if there are none, detect the eclipse runtime 3213 if (vmDefs.getValidVMList().isEmpty()) { 3214 // calling out to detectEclipseRuntime() could allow clients to change 3215 // VM settings (i.e. call back into change VM settings). 3216 VMListener listener = new VMListener(); 3217 addVMInstallChangedListener(listener); 3218 setPref = true; 3219 VMStandin runtime = detectEclipseRuntime(); 3220 removeVMInstallChangedListener(listener); 3221 if (!listener.isChanged()) { 3222 if (runtime != null) { 3223 updateCompliance = true; 3224 vmDefs.addVM(runtime); 3225 vmDefs.setDefaultVMInstallCompositeID(getCompositeIdFromVM(runtime)); 3226 } 3227 } else { 3228 // VMs were changed - reflect current settings 3229 addPersistedVMs(vmDefs); 3230 vmDefs.setDefaultVMInstallCompositeID(fgDefaultVMId); 3231 updateCompliance = fgDefaultVMId != null; 3232 } 3233 } 3234 // 4. load contributed VM installs 3235 addVMExtensions(vmDefs); 3236 // 5. verify default VM is valid 3237 String defId = vmDefs.getDefaultVMInstallCompositeID(); 3238 boolean validDef = false; 3239 if (defId != null) { 3240 Iterator<IVMInstall> iterator = vmDefs.getValidVMList().iterator(); 3241 while (iterator.hasNext()) { 3242 IVMInstall vm = iterator.next(); 3243 if (getCompositeIdFromVM(vm).equals(defId)) { 3244 validDef = true; 3245 break; 3246 } 3247 } 3248 } 3249 if (!validDef) { 3250 // use the first as the default 3251 setPref = true; 3252 List<IVMInstall> list = vmDefs.getValidVMList(); 3253 if (!list.isEmpty()) { 3254 IVMInstall vm = list.get(0); 3255 vmDefs.setDefaultVMInstallCompositeID(getCompositeIdFromVM(vm)); 3256 } 3257 } 3258 fgDefaultVMId = vmDefs.getDefaultVMInstallCompositeID(); 3259 fgDefaultVMConnectorId = vmDefs.getDefaultVMInstallConnectorTypeID(); 3260 3261 // Create the underlying VMs for each valid VM 3262 List<IVMInstall> vmList = vmDefs.getValidVMList(); 3263 Iterator<IVMInstall> vmListIterator = vmList.iterator(); 3264 while (vmListIterator.hasNext()) { 3265 VMStandin vmStandin = (VMStandin) vmListIterator.next(); 3266 vmStandin.convertToRealVM(); 3267 } 3268 3269 3270 } catch (IOException e) { 3271 LaunchingPlugin.log(e); 3272 } 3273 } finally { 3274 fgInitializingVMs = false; 3275 } 3276 } 3277 } 3278 if (vmDefs != null) { 3279 // notify of initial VMs for backwards compatibility 3280 IVMInstallType[] installTypes = getVMInstallTypes(); 3281 for (int i = 0; i < installTypes.length; i++) { 3282 IVMInstallType type = installTypes[i]; 3283 IVMInstall[] installs = type.getVMInstalls(); 3284 for (int j = 0; j < installs.length; j++) { 3285 fireVMAdded(installs[j]); 3286 } 3287 } 3288 3289 // save settings if required 3290 if (setPref) { 3291 try { 3292 String xml = vmDefs.getAsXML(); 3293 InstanceScope.INSTANCE.getNode(LaunchingPlugin.ID_PLUGIN).put(PREF_VM_XML, xml); 3294 } catch (CoreException e) { 3295 LaunchingPlugin.log(e); 3296 } 3297 } 3298 // update compliance if required 3299 if (updateCompliance) { 3300 updateCompliance(getDefaultVMInstall()); 3301 } 3302 } 3303 } 3304 3305 /** 3306 * Update compiler compliance settings based on the given VM. 3307 * 3308 * @param vm the backing {@link IVMInstall} 3309 */ updateCompliance(IVMInstall vm)3310 private static void updateCompliance(IVMInstall vm) { 3311 if (LaunchingPlugin.isVMLogging()) { 3312 LaunchingPlugin.log("Compliance needs an update."); //$NON-NLS-1$ 3313 } 3314 if (vm instanceof IVMInstall2) { 3315 String javaVersion = ((IVMInstall2)vm).getJavaVersion(); 3316 if (javaVersion != null) { 3317 String compliance = null; 3318 if (javaVersion.startsWith(JavaCore.VERSION_1_5)) { 3319 compliance = JavaCore.VERSION_1_5; 3320 } else if (javaVersion.startsWith(JavaCore.VERSION_1_6)) { 3321 compliance = JavaCore.VERSION_1_6; 3322 } else if (javaVersion.startsWith(JavaCore.VERSION_1_7)) { 3323 compliance = JavaCore.VERSION_1_7; 3324 } else if (javaVersion.startsWith(JavaCore.VERSION_1_8)) { 3325 compliance = JavaCore.VERSION_1_8; 3326 } else if (javaVersion.startsWith(JavaCore.VERSION_9) 3327 && (javaVersion.length() == JavaCore.VERSION_9.length() || javaVersion.charAt(JavaCore.VERSION_9.length()) == '.')) { 3328 compliance = JavaCore.VERSION_9; 3329 } else if (javaVersion.startsWith(JavaCore.VERSION_10) 3330 && (javaVersion.length() == JavaCore.VERSION_10.length() || javaVersion.charAt(JavaCore.VERSION_10.length()) == '.')) { 3331 compliance = JavaCore.VERSION_10; 3332 } else if (javaVersion.startsWith(JavaCore.VERSION_11) 3333 && (javaVersion.length() == JavaCore.VERSION_11.length() || javaVersion.charAt(JavaCore.VERSION_11.length()) == '.')) { 3334 compliance = JavaCore.VERSION_11; 3335 } else if (javaVersion.startsWith(JavaCore.VERSION_12) 3336 && (javaVersion.length() == JavaCore.VERSION_12.length() || javaVersion.charAt(JavaCore.VERSION_12.length()) == '.')) { 3337 compliance = JavaCore.VERSION_12; 3338 } else if (javaVersion.startsWith(JavaCore.VERSION_13) 3339 && (javaVersion.length() == JavaCore.VERSION_13.length() || javaVersion.charAt(JavaCore.VERSION_13.length()) == '.')) { 3340 compliance = JavaCore.VERSION_13; 3341 } else if (javaVersion.startsWith(JavaCore.VERSION_14) 3342 && (javaVersion.length() == JavaCore.VERSION_14.length() || javaVersion.charAt(JavaCore.VERSION_14.length()) == '.')) { 3343 compliance = JavaCore.VERSION_14; 3344 } else { 3345 compliance = JavaCore.VERSION_14; // use latest by default 3346 } 3347 3348 Hashtable<String, String> options= JavaCore.getOptions(); 3349 3350 org.osgi.service.prefs.Preferences bundleDefaults = BundleDefaultsScope.INSTANCE.getNode(JavaCore.PLUGIN_ID); 3351 3352 boolean isDefault = 3353 equals(JavaCore.COMPILER_COMPLIANCE, options, bundleDefaults) && 3354 equals(JavaCore.COMPILER_SOURCE, options, bundleDefaults) && 3355 equals(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, options, bundleDefaults) && 3356 equals(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER, options, bundleDefaults) && 3357 equals(JavaCore.COMPILER_PB_ENUM_IDENTIFIER, options, bundleDefaults); 3358 if (JavaCore.compareJavaVersions(compliance, JavaCore.VERSION_10) > 0) { 3359 isDefault = isDefault && equals(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, options, bundleDefaults) 3360 && equals(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, options, bundleDefaults); 3361 } 3362 // only update the compliance settings if they are default settings, otherwise the 3363 // settings have already been modified by a tool or user 3364 if (LaunchingPlugin.isVMLogging()) { 3365 LaunchingPlugin.log("Compliance to be updated is: " + compliance); //$NON-NLS-1$ 3366 } 3367 if (isDefault) { 3368 JavaCore.setComplianceOptions(compliance, options); 3369 JavaCore.setOptions(options); 3370 if (LaunchingPlugin.isVMLogging()) { 3371 LaunchingPlugin.log("Compliance Options are updated."); //$NON-NLS-1$ 3372 } 3373 } 3374 3375 } 3376 } 3377 } 3378 3379 /** 3380 * Creates a new VM based on the attributes specified in the given execution 3381 * environment description file. The format of the file is defined by 3382 * <code>http://wiki.eclipse.org/Execution_Environment_Descriptions</code>. 3383 * 3384 * @param eeFile VM definition file 3385 * @param name name for the VM, or <code>null</code> if a default name should be assigned 3386 * @param id id to assign to the new VM 3387 * @return VM stand-in 3388 * @exception CoreException if unable to create a VM from the given definition file 3389 * @since 3.4 3390 */ createVMFromDefinitionFile(File eeFile, String name, String id)3391 public static VMStandin createVMFromDefinitionFile(File eeFile, String name, String id) throws CoreException { 3392 ExecutionEnvironmentDescription description = new ExecutionEnvironmentDescription(eeFile); 3393 IStatus status = EEVMType.validateDefinitionFile(description); 3394 if (status.isOK()) { 3395 VMStandin standin = new VMStandin(getVMInstallType(EEVMType.ID_EE_VM_TYPE), id); 3396 if (name != null && name.length() > 0){ 3397 standin.setName(name); 3398 } else { 3399 name = description.getProperty(ExecutionEnvironmentDescription.EE_NAME); 3400 if (name == null) { 3401 name = eeFile.getName(); 3402 } 3403 standin.setName(name); 3404 } 3405 String home = description.getProperty(ExecutionEnvironmentDescription.JAVA_HOME); 3406 standin.setInstallLocation(new File(home)); 3407 standin.setLibraryLocations(description.getLibraryLocations()); 3408 standin.setVMArgs(description.getVMArguments()); 3409 standin.setJavadocLocation(EEVMType.getJavadocLocation(description.getProperties())); 3410 standin.setAttribute(EEVMInstall.ATTR_EXECUTION_ENVIRONMENT_ID, description.getProperty(ExecutionEnvironmentDescription.CLASS_LIB_LEVEL)); 3411 File exe = description.getExecutable(); 3412 if (exe == null) { 3413 exe = description.getConsoleExecutable(); 3414 } 3415 if (exe != null) { 3416 try { 3417 standin.setAttribute(EEVMInstall.ATTR_JAVA_EXE, exe.getCanonicalPath()); 3418 } catch (IOException e) { 3419 throw new CoreException(new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 3420 LaunchingMessages.JavaRuntime_24, e)); 3421 } 3422 } 3423 standin.setAttribute(EEVMInstall.ATTR_JAVA_VERSION, description.getProperty(ExecutionEnvironmentDescription.LANGUAGE_LEVEL)); 3424 standin.setAttribute(EEVMInstall.ATTR_DEFINITION_FILE, eeFile.getPath()); 3425 standin.setAttribute(EEVMInstall.ATTR_DEBUG_ARGS, description.getProperty(ExecutionEnvironmentDescription.DEBUG_ARGS)); 3426 return standin; 3427 } 3428 throw new CoreException(status); 3429 } 3430 3431 private static final String BLANK = " "; //$NON-NLS-1$ 3432 private static final String COMMA = ","; //$NON-NLS-1$ 3433 private static final String OPTION_START = "--"; //$NON-NLS-1$ 3434 private static final String ADD_MODULES = "--add-modules "; //$NON-NLS-1$ 3435 private static final String LIMIT_MODULES = "--limit-modules "; //$NON-NLS-1$ 3436 3437 /** 3438 * Returns the module-related command line options for the configuration that are needed at runtime as equivalents of those options specified by 3439 * {@link IClasspathAttribute}s of the following names: 3440 * <ul> 3441 * <li>{@link IClasspathAttribute#ADD_EXPORTS}</li> 3442 * <li>{@link IClasspathAttribute#ADD_READS}</li> 3443 * <li>{@link IClasspathAttribute#LIMIT_MODULES}</li> 3444 * </ul> 3445 * {@link IClasspathAttribute#PATCH_MODULE} is not handled here, but in 3446 * {@link AbstractJavaLaunchConfigurationDelegate#getModuleCLIOptions(ILaunchConfiguration)}, which then collates all options referring to the 3447 * same module. 3448 * 3449 * @since 3.10 3450 */ getModuleCLIOptions(ILaunchConfiguration configuration)3451 public static String getModuleCLIOptions(ILaunchConfiguration configuration) { 3452 StringBuilder cliOptionString = new StringBuilder(); 3453 try { 3454 IRuntimeClasspathEntry[] entries; 3455 entries = JavaRuntime.computeUnresolvedRuntimeClasspath(configuration); 3456 3457 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); 3458 for (IRuntimeClasspathEntry iRuntimeClasspathEntry : entries) { 3459 IClasspathEntry classpathEntry = iRuntimeClasspathEntry.getClasspathEntry(); 3460 if (classpathEntry != null && classpathEntry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { 3461 IResource res = root.findMember(classpathEntry.getPath()); 3462 IJavaProject jp = (IJavaProject) JavaCore.create(res); 3463 if (jp.isOpen()) { 3464 IClasspathEntry[] rawClasspath = jp.getRawClasspath(); 3465 for (IClasspathEntry iClasspathEntry : rawClasspath) { 3466 if (iClasspathEntry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { 3467 if (JavaRuntime.JRE_CONTAINER.equals(iClasspathEntry.getPath().segment(0))) { 3468 String cliOptions = getModuleCLIOptions(jp, iClasspathEntry); 3469 if (cliOptionString.length() > 0 && cliOptions.length() > 0) { 3470 cliOptionString.append(" "); //$NON-NLS-1$ 3471 } 3472 cliOptionString.append(cliOptions); 3473 } 3474 } 3475 } 3476 } 3477 3478 } 3479 } 3480 } 3481 catch (CoreException e) { 3482 LaunchingPlugin.log(e); 3483 } 3484 return cliOptionString.toString().trim(); 3485 } 3486 3487 /** 3488 * Returns the module-related command line options that are needed at runtime as equivalents of those options specified by 3489 * {@link IClasspathAttribute}s of the following names: 3490 * <ul> 3491 * <li>{@link IClasspathAttribute#ADD_EXPORTS}</li> 3492 * <li>{@link IClasspathAttribute#ADD_OPENS}</li> 3493 * <li>{@link IClasspathAttribute#ADD_READS}</li> 3494 * <li>{@link IClasspathAttribute#LIMIT_MODULES}</li> 3495 * </ul> 3496 * <p> 3497 * Note that the {@link IClasspathAttribute#LIMIT_MODULES} value may be split into an {@code --add-modules} part and a {@code --limit-modules} 3498 * part. 3499 * </p> 3500 * 3501 * @param project 3502 * the project holding the main class to be launched 3503 * @param systemLibrary 3504 * the classpath entry of the given project which represents the JRE System Library 3505 * @return module-related command line options suitable for running the application. 3506 * @throws JavaModelException 3507 * when access to the classpath or module description of the given project fails. 3508 */ getModuleCLIOptions(IJavaProject project, IClasspathEntry systemLibrary)3509 private static String getModuleCLIOptions(IJavaProject project, IClasspathEntry systemLibrary) throws JavaModelException { 3510 StringBuilder buf = new StringBuilder(); 3511 for (IClasspathEntry classpathEntry : project.getRawClasspath()) { 3512 for (IClasspathAttribute classpathAttribute : classpathEntry.getExtraAttributes()) { 3513 String optName = classpathAttribute.getName(); 3514 switch (optName) { 3515 case IClasspathAttribute.ADD_EXPORTS: 3516 case IClasspathAttribute.ADD_OPENS: 3517 case IClasspathAttribute.ADD_READS: { 3518 String readModules = classpathAttribute.getValue(); 3519 int equalsIdx = readModules.indexOf('='); 3520 if (equalsIdx != -1) { 3521 for (String readModule : readModules.split(":")) { //$NON-NLS-1$ 3522 buf.append(OPTION_START).append(optName).append(BLANK).append(readModule).append(BLANK); 3523 } 3524 } else { 3525 buf.append(OPTION_START).append(optName).append(BLANK).append(readModules).append(BLANK); 3526 } 3527 break; 3528 } 3529 // case IClasspathAttribute.PATCH_MODULE: handled in 3530 // org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate.getModuleCLIOptions(ILaunchConfiguration) 3531 case IClasspathAttribute.LIMIT_MODULES: 3532 addLimitModules(buf, project, systemLibrary, classpathAttribute.getValue()); 3533 break; 3534 } 3535 } 3536 } 3537 return buf.toString().trim(); 3538 } addLimitModules(StringBuilder buf, IJavaProject prj, IClasspathEntry systemLibrary, String value)3539 private static void addLimitModules(StringBuilder buf, IJavaProject prj, IClasspathEntry systemLibrary, String value) throws JavaModelException { 3540 String[] modules = value.split(COMMA); 3541 boolean isUnnamed = prj.getModuleDescription() == null; 3542 if (isUnnamed) { 3543 Set<String> selected = new HashSet<>(Arrays.asList(modules)); 3544 List<IPackageFragmentRoot> allSystemRoots = Arrays.asList(prj.findUnfilteredPackageFragmentRoots(systemLibrary)); 3545 Set<String> defaultModules = getDefaultModules(allSystemRoots); 3546 Set<String> limit = new HashSet<>(defaultModules); // contains some redundancy, but is no full closure 3547 3548 // selected contains the minimal representation, now compute the transitive closure for comparison with semi-closed defaultModules: 3549 Map<String, IModuleDescription> allModules = allSystemRoots.stream() // 3550 .map(r -> r.getModuleDescription()) // 3551 .filter(Objects::nonNull) // 3552 .collect(Collectors.toMap(IModuleDescription::getElementName, module -> module)); 3553 Set<String> selectedClosure = closure(selected, new HashSet<>(), allModules); 3554 3555 if (limit.retainAll(selectedClosure)) { // limit = selected ∩ default -- only add the option, if limit ⊂ default 3556 if (limit.isEmpty()) { 3557 throw new IllegalArgumentException("Cannot hide all modules, at least java.base is required"); //$NON-NLS-1$ 3558 } 3559 buf.append(LIMIT_MODULES).append(joinedSortedList(reduceNames(limit, allModules.values()))).append(BLANK); 3560 } 3561 3562 selectedClosure.removeAll(defaultModules); 3563 if (!selectedClosure.isEmpty()) { // add = selected \ default 3564 buf.append(ADD_MODULES).append(joinedSortedList(selectedClosure)).append(BLANK); 3565 } 3566 } else { 3567 Arrays.sort(modules); 3568 buf.append(LIMIT_MODULES).append(String.join(COMMA, modules)).append(BLANK); 3569 } 3570 } 3571 closure(Collection<String> moduleNames, Set<String> collected, Map<String, IModuleDescription> allModules)3572 private static Set<String> closure(Collection<String> moduleNames, Set<String> collected, Map<String, IModuleDescription> allModules) { 3573 for (String name : moduleNames) { 3574 if (collected.add(name)) { 3575 IModuleDescription module = allModules.get(name); 3576 if (module != null) { 3577 try { 3578 closure(Arrays.asList(module.getRequiredModuleNames()), collected, allModules); 3579 } catch (JavaModelException e) { 3580 LaunchingPlugin.log(e); 3581 } 3582 } 3583 } 3584 } 3585 return collected; 3586 } 3587 reduceNames(Collection<String> names, Collection<IModuleDescription> allModules)3588 private static Collection<String> reduceNames(Collection<String> names, Collection<IModuleDescription> allModules) { 3589 // build a reverse dependency tree: 3590 Map<String, List<String>> moduleRequiredByModules = new HashMap<>(); 3591 for (IModuleDescription module : allModules) { 3592 if (!names.contains(module.getElementName())) { 3593 continue; 3594 } 3595 try { 3596 for (String required : module.getRequiredModuleNames()) { 3597 List<String> dominators = moduleRequiredByModules.get(required); 3598 if (dominators == null) { 3599 moduleRequiredByModules.put(required, dominators = new ArrayList<>()); 3600 } 3601 dominators.add(module.getElementName()); 3602 } 3603 } catch (CoreException e) { 3604 LaunchingPlugin.log(e); 3605 return names; // unreduced 3606 } 3607 } 3608 // use the tree to find and eliminate redundancy: 3609 List<String> reduced = new ArrayList<>(); 3610 outer: for (String name : names) { 3611 List<String> dominators = moduleRequiredByModules.get(name); 3612 if (dominators != null) { 3613 for (String dominator : dominators) { 3614 if (names.contains(dominator)) { 3615 continue outer; 3616 } 3617 } 3618 } 3619 reduced.add(name); 3620 } 3621 return reduced; 3622 } 3623 getDefaultModules(List<IPackageFragmentRoot> allSystemRoots)3624 private static Set<String> getDefaultModules(List<IPackageFragmentRoot> allSystemRoots) throws JavaModelException { 3625 HashMap<String, String[]> moduleDescriptions = new HashMap<>(); 3626 for (IPackageFragmentRoot packageFragmentRoot : allSystemRoots) { 3627 IModuleDescription module = packageFragmentRoot.getModuleDescription(); 3628 if (module != null) { 3629 moduleDescriptions.put(module.getElementName(), module.getRequiredModuleNames()); 3630 } 3631 } 3632 HashSet<String> result = new HashSet<>(); 3633 HashSet<String> todo = new HashSet<>(JavaProject.defaultRootModules(allSystemRoots)); 3634 while (!todo.isEmpty()) { 3635 HashSet<String> more = new HashSet<>(); 3636 for (String s : todo) { 3637 if (result.add(s)) { 3638 String[] requiredModules = moduleDescriptions.get(s); 3639 if (requiredModules != null) { 3640 Collections.addAll(more, requiredModules); 3641 } 3642 } 3643 } 3644 todo = more; 3645 } 3646 return result; 3647 } 3648 joinedSortedList(Collection<String> list)3649 private static String joinedSortedList(Collection<String> list) { 3650 String[] limitArray = list.toArray(new String[list.size()]); 3651 Arrays.sort(limitArray); 3652 return String.join(COMMA, limitArray); 3653 } 3654 } 3655