/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching * external annotations to a JRE container *******************************************************************************/ package org.eclipse.jdt.internal.launching; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.ClasspathContainerInitializer; import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.jdt.launching.IVMInstallType; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.launching.LibraryLocation; import org.eclipse.jdt.launching.VMStandin; import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager; import org.eclipse.osgi.util.NLS; /** * Resolves a container for a JRE classpath container entry. */ public class JREContainerInitializer extends ClasspathContainerInitializer { /** * @see ClasspathContainerInitializer#initialize(IPath, IJavaProject) */ @Override public void initialize(IPath containerPath, IJavaProject project) throws CoreException { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("<JRE_CONTAINER> initialize()"); //$NON-NLS-1$ LaunchingPlugin.trace("\tPath: " + containerPath.toString()); //$NON-NLS-1$ LaunchingPlugin.trace("\tProj: " + project.getProject().getName()); //$NON-NLS-1$ } int size = containerPath.segmentCount(); if (size > 0) { if (containerPath.segment(0).equals(JavaRuntime.JRE_CONTAINER)) { IVMInstall vm = resolveVM(containerPath); JREContainer container = null; if (vm != null) { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\tResolved VM: " + vm.getName()); //$NON-NLS-1$ } container = new JREContainer(vm, containerPath, project); } else { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\t*** FAILED RESOLVE VM ***"); //$NON-NLS-1$ } } JavaCore.setClasspathContainer(containerPath, new IJavaProject[] {project}, new IClasspathContainer[] {container}, null); } else { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\t*** INVALID JRE CONTAINER PATH ***"); //$NON-NLS-1$ } } } else { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\t*** NO SEGMENTS IN CONTAINER PATH ***"); //$NON-NLS-1$ } } } /** * Sets the specified class path container for all of the given projects. * * @param containerPath JRE container path * @param projects projects set the container on * @throws CoreException on failure */ public void initialize(IPath containerPath, IJavaProject[] projects) throws CoreException { int size = containerPath.segmentCount(); if (size > 0) { if (containerPath.segment(0).equals(JavaRuntime.JRE_CONTAINER)) { int length = projects.length; IVMInstall vm = resolveVM(containerPath); IClasspathContainer[] containers = new JREContainer[length]; if (vm != null) { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\tResolved VM: " + vm.getName()); //$NON-NLS-1$ } for (int i=0; i<length; i++) { containers[i] = new JREContainer(vm, containerPath, projects[i]); } } else { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\t*** FAILED RESOLVE VM ***"); //$NON-NLS-1$ } } JavaCore.setClasspathContainer(containerPath, projects, containers, null); } else { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\t*** INVALID JRE CONTAINER PATH ***"); //$NON-NLS-1$ } } } else { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\t*** NO SEGMENTS IN CONTAINER PATH ***"); //$NON-NLS-1$ } } } /** * Returns the VM install associated with the container path, or <code>null</code> * if it does not exist. * @param containerPath the path to the container * @return the {@link IVMInstall} or <code>null</code> */ public static IVMInstall resolveVM(IPath containerPath) { IVMInstall vm = null; if (containerPath.segmentCount() > 1) { // specific JRE String id = getExecutionEnvironmentId(containerPath); if (id != null) { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("<JRE_CONTAINER> resolveVM(IPath)"); //$NON-NLS-1$ LaunchingPlugin.trace("\tEE: " + id); //$NON-NLS-1$ } IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager(); IExecutionEnvironment environment = manager.getEnvironment(id); if (environment != null) { vm = resolveVM(environment); } else { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\t*** NO ENVIRONMENT ***"); //$NON-NLS-1$ } } } else { String vmTypeId = getVMTypeId(containerPath); String vmName = getVMName(containerPath); IVMInstallType vmType = JavaRuntime.getVMInstallType(vmTypeId); if (vmType != null) { vm = vmType.findVMInstallByName(vmName); } } } else { // workspace default JRE vm = JavaRuntime.getDefaultVMInstall(); } return vm; } /** * Returns the VM install bound to the given execution environment * or <code>null</code>. * * @param environment the environment * @return VM install or <code>null</code> * @since 3.2 */ public static IVMInstall resolveVM(IExecutionEnvironment environment) { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("<JRE_CONTAINER> resolveVM(IExecutionEnvironment)"); //$NON-NLS-1$ } IVMInstall vm = environment.getDefaultVM(); if (vm == null) { IVMInstall[] installs = environment.getCompatibleVMs(); // take the first strictly compatible VM if there is no default if (installs.length == 0 && LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\t*** NO COMPATIBLE VMS ***"); //$NON-NLS-1$ } for (int i = 0; i < installs.length; i++) { IVMInstall install = installs[i]; if (environment.isStrictlyCompatible(install)) { vm = install; if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\tPerfect Match: " + vm.getName()); //$NON-NLS-1$ } break; } } //try the default VM install: https://bugs.eclipse.org/bugs/show_bug.cgi?id=371300 // if default vm is a match https://bugs.eclipse.org/bugs/show_bug.cgi?id=484026 if (vm == null && installs.length > 0 && Arrays.asList(installs).contains(JavaRuntime.getDefaultVMInstall())) { vm = JavaRuntime.getDefaultVMInstall(); } // use the first VM failing that if (vm == null && installs.length > 0) { vm = installs[0]; if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\tFirst Match: " + vm.getName()); //$NON-NLS-1$ } } } else { if (LaunchingPlugin.DEBUG_JRE_CONTAINER) { LaunchingPlugin.trace("\tUser Default VM: " + vm.getName()); //$NON-NLS-1$ } } return vm; } /** * Returns the segment from the path containing the execution environment id * or <code>null</code> * * @param path container path * @return EE id */ public static String getExecutionEnvironmentId(IPath path) { String name = getVMName(path); if (name != null) { name = decodeEnvironmentId(name); IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager(); IExecutionEnvironment environment = manager.getEnvironment(name); if (environment != null) { return environment.getId(); } } return null; } /** * Returns whether the given path identifies a VM by execution environment. * * @param path the path * @return whether the given path identifies a VM by execution environment */ public static boolean isExecutionEnvironment(IPath path) { return getExecutionEnvironmentId(path) != null; } /** * Escapes forward slashes in environment id. * * @param id the environment id * @return escaped name */ public static String encodeEnvironmentId(String id) { return id.replace('/', '%'); } public static String decodeEnvironmentId(String id) { return id.replace('%', '/'); } /** * Returns the VM type identifier from the given container ID path. * * @param path the path * @return the VM type identifier from the given container ID path */ public static String getVMTypeId(IPath path) { return path.segment(1); } /** * Returns the VM name from the given container ID path. * * @param path the path * @return the VM name from the given container ID path */ public static String getVMName(IPath path) { return path.segment(2); } /** * The container can be updated if it refers to an existing VM. * * @see org.eclipse.jdt.core.ClasspathContainerInitializer#canUpdateClasspathContainer(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject) */ @Override public boolean canUpdateClasspathContainer(IPath containerPath, IJavaProject project) { if (containerPath != null && containerPath.segmentCount() > 0) { if (JavaRuntime.JRE_CONTAINER.equals(containerPath.segment(0))) { return resolveVM(containerPath) != null; } } return false; } private static final IStatus READ_ONLY= new Status(IStatus.ERROR, LaunchingPlugin.ID_PLUGIN, ClasspathContainerInitializer.ATTRIBUTE_READ_ONLY, new String(), null); private static final IStatus NOT_SUPPORTED= new Status(IStatus.ERROR, LaunchingPlugin.ID_PLUGIN, ClasspathContainerInitializer.ATTRIBUTE_NOT_SUPPORTED, new String(), null); /* (non-Javadoc) * @see org.eclipse.jdt.core.ClasspathContainerInitializer#getAccessRulesStatus(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject) */ @Override public IStatus getAccessRulesStatus(IPath containerPath, IJavaProject project) { return READ_ONLY; } /* (non-Javadoc) * @see org.eclipse.jdt.core.ClasspathContainerInitializer#getSourceAttachmentStatus(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject) */ @Override public IStatus getSourceAttachmentStatus(IPath containerPath, IJavaProject project) { return Status.OK_STATUS; } /* (non-Javadoc) * @see org.eclipse.jdt.core.ClasspathContainerInitializer#getAttributeStatus(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject, java.lang.String) */ @Override public IStatus getAttributeStatus(IPath containerPath, IJavaProject project, String attributeKey) { if (attributeKey.equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) { return Status.OK_STATUS; } if (attributeKey.equals(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH)) { return Status.OK_STATUS; } if (attributeKey.equals(JavaRuntime.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY)) { return Status.OK_STATUS; } return NOT_SUPPORTED; } /** * @see org.eclipse.jdt.core.ClasspathContainerInitializer#requestClasspathContainerUpdate(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject, org.eclipse.jdt.core.IClasspathContainer) */ @Override public void requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion) throws CoreException { IVMInstall vm = resolveVM(containerPath); if (vm == null) { IStatus status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), IJavaLaunchConfigurationConstants.ERR_VM_INSTALL_DOES_NOT_EXIST, NLS.bind(LaunchingMessages.JREContainerInitializer_JRE_referenced_by_classpath_container__0__does_not_exist__1, new String[]{containerPath.toString()}), null); throw new CoreException(status); } // update of the VM with new library locations IClasspathEntry[] entries = containerSuggestion.getClasspathEntries(); LibraryLocation[] libs = new LibraryLocation[entries.length]; for (int i = 0; i < entries.length; i++) { IClasspathEntry entry = entries[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { IPath path = entry.getPath(); File lib = path.toFile(); if (lib.exists() && lib.isFile()) { IPath srcPath = entry.getSourceAttachmentPath(); if (srcPath == null) { srcPath = Path.EMPTY; } IPath rootPath = entry.getSourceAttachmentRootPath(); if (rootPath == null) { rootPath = Path.EMPTY; } URL javadocLocation = null; IPath externalAnnotations = null; IClasspathAttribute[] extraAttributes = entry.getExtraAttributes(); for (int j = 0; j < extraAttributes.length; j++) { IClasspathAttribute attribute = extraAttributes[j]; if (attribute.getName().equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) { String url = attribute.getValue(); if (url != null && url.trim().length() > 0) { try { javadocLocation = new URL(url); } catch (MalformedURLException e) { LaunchingPlugin.log(e); } } } else if (attribute.getName().equals(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH)) { String xpath = attribute.getValue(); if (null != xpath && xpath.trim().length() > 0) { try { externalAnnotations = Path.fromPortableString(xpath); } catch (Exception x) { LaunchingPlugin.log(x); } } } } libs[i] = new LibraryLocation(path, srcPath, rootPath, javadocLocation, null, externalAnnotations); } else { IStatus status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR, NLS.bind(LaunchingMessages.JREContainerInitializer_Classpath_entry__0__does_not_refer_to_an_existing_library__2, new String[]{entry.getPath().toString()}), null); throw new CoreException(status); } } else { IStatus status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), IJavaLaunchConfigurationConstants.ERR_INTERNAL_ERROR, NLS.bind(LaunchingMessages.JREContainerInitializer_Classpath_entry__0__does_not_refer_to_a_library__3, new String[]{entry.getPath().toString()}), null); throw new CoreException(status); } } VMStandin standin = new VMStandin(vm); standin.setLibraryLocations(libs); standin.convertToRealVM(); JavaRuntime.saveVMConfiguration(); } /** * @see org.eclipse.jdt.core.ClasspathContainerInitializer#getDescription(org.eclipse.core.runtime.IPath, org.eclipse.jdt.core.IJavaProject) */ @Override public String getDescription(IPath containerPath, IJavaProject project) { String tag = getExecutionEnvironmentId(containerPath); if (tag == null && containerPath.segmentCount() > 2) { tag = getVMName(containerPath); } if (tag != null) { return NLS.bind(LaunchingMessages.JREContainer_JRE_System_Library_1, new String[]{tag}); } return LaunchingMessages.JREContainerInitializer_Default_System_Library_1; } }