/*******************************************************************************
* Copyright © 2011, 2013 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
*
*******************************************************************************/
package org.eclipse.edt.ide.testserver;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.edt.compiler.tools.IRUtils;
import org.eclipse.edt.ide.core.internal.model.EGLProject;
import org.eclipse.edt.ide.core.model.EGLCore;
import org.eclipse.edt.ide.core.model.EGLModelException;
import org.eclipse.edt.ide.core.model.IEGLPathEntry;
import org.eclipse.edt.ide.core.model.IEGLProject;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
/**
* Utility methods for building classpaths used by JDT. Clients may use this class.
*/
public class ClasspathUtil {
private ClasspathUtil() {
// No instances.
}
public final static char[] SUFFIX_egldd = ".egldd".toCharArray(); //$NON-NLS-1$
public final static char[] SUFFIX_EGLDD = ".EGLDD".toCharArray(); //$NON-NLS-1$
/**
* The classpath entries that are applied to all test servers.
*/
private static List<String> testServerBaseEntries;
/**
* Creates a classpath to be added to a a launch configuration for running the Jetty server. The appropriate Jetty,
* servlet, and EDT plug-ins will be added to the classpath, as well as ensuring any Java projects on the EGL build path
* are included in the classpath if they aren't already. Finally, classpath contributions from
* {@link AbstractTestServerContribution#getClasspathAdditions(TestServerConfiguration)} will be appended at the end, so
* long as the contributions are valid.
*
* @param config The test server configuration.
* @param classpath A list that will receive the new entries.
* @throws CoreException
*/
public static void buildClasspath(TestServerConfiguration config, List<String> classpath) throws CoreException {
IProject project = config.getProject();
String entry;
// The project's JRE (not required for execution, but required for source lookup).
try {
IRuntimeClasspathEntry jreEntry = JavaRuntime.computeJREEntry(JavaCore.create(project));
if (jreEntry != null) {
classpath.add(jreEntry.getMemento());
}
}
catch (CoreException ce) {
TestServerPlugin.getDefault().log(ce);
}
if (testServerBaseEntries == null) {
testServerBaseEntries = new ArrayList<String>(20);
// Add all org.eclipse.jetty.* plug-ins.
Bundle[] bundles = TestServerPlugin.getDefault().getBundle().getBundleContext().getBundles();
for (Bundle bundle : bundles) {
if (bundle.getSymbolicName().startsWith("org.eclipse.jetty.")) { //$NON-NLS-1$
entry = getClasspathEntry(bundle);
if (entry != null) {
testServerBaseEntries.add(entry);
}
}
}
entry = getClasspathEntry("javax.servlet"); //$NON-NLS-1$
if (entry != null) {
testServerBaseEntries.add(entry);
}
entry = getClasspathEntry("org.eclipse.edt.ide.testserver"); //$NON-NLS-1$
if (entry != null) {
testServerBaseEntries.add(entry);
}
}
classpath.addAll(testServerBaseEntries);
// The main project.
classpath.add(getWorkspaceProjectClasspathEntry(project.getName()));
// Add any contributed classpath entries.
for (AbstractTestServerContribution contrib : TestServerPlugin.getContributions()) {
String[] extraClasspath = contrib.getClasspathAdditions(config);
if (extraClasspath != null && extraClasspath.length > 0) {
for (String next : extraClasspath) {
if (!classpath.contains(next)) {
// Validate the entry; bad entries will prevent the process from launching.
if (classpathEntryIsValid(next, project)) {
classpath.add(next);
}
else {
TestServerPlugin.getDefault().logWarning(NLS.bind(TestServerMessages.InvalidClasspathEntry, next));
}
}
}
}
}
}
public static void addEGLPathToJavaPathIfNecessary(IJavaProject javaProject, IProject currProject, Set<IProject> seen, List<String> classpath) {
if (seen.contains(currProject)) {
return;
}
seen.add(currProject);
try {
if (currProject.hasNature(EGLCore.NATURE_ID)) {
IEGLProject eglProject = EGLCore.create(currProject);
for (IEGLPathEntry pathEntry : eglProject.getResolvedEGLPath(true)) {
if (pathEntry.getEntryKind() == IEGLPathEntry.CPE_PROJECT) {
IPath path = pathEntry.getPath();
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
try {
if (resource != null && resource.getType() == IResource.PROJECT && !seen.contains(resource)
&& ((IProject)resource).hasNature(JavaCore.NATURE_ID) && !javaProject.isOnClasspath(resource)) {
classpath.add(getWorkspaceProjectClasspathEntry(resource.getName()));
addEGLPathToJavaPathIfNecessary(javaProject, (IProject)resource, seen, classpath);
}
}
catch (CoreException ce) {
}
}
}
}
}
catch (EGLModelException e) {
}
catch (CoreException e) {
}
}
public static String getClasspathEntry(String pluginName) {
Bundle bundle = Platform.getBundle(pluginName);
if (bundle != null) {
return getClasspathEntry(bundle);
}
TestServerPlugin.getDefault().log(NLS.bind(TestServerMessages.CouldNotGetPluginPath, pluginName), null);
return null;
}
public static String getClasspathEntry(Bundle bundle) {
try {
File file = FileLocator.getBundleFile(bundle);
String path = file.getAbsolutePath();
if (file.isDirectory()) {
if (!path.endsWith( File.separator)) {
path += File.separator;
}
path += "bin"; //$NON-NLS-1$
}
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><runtimeClasspathEntry externalArchive=\"" + path + "\" path=\"3\" type=\"2\"/>"; //$NON-NLS-1$ //$NON-NLS-2$
}
catch (IOException e) {
TestServerPlugin.getDefault().log(NLS.bind(TestServerMessages.CouldNotGetPluginPath, bundle.getSymbolicName()), e);
return null;
}
}
public static String getWorkspaceProjectClasspathEntry(String projectName) {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><runtimeClasspathEntry id=\"org.eclipse.jdt.launching.classpathentry.defaultClasspath\">" + //$NON-NLS-1$
"<memento exportedEntriesOnly=\"false\" project=\"" + projectName + "\"/></runtimeClasspathEntry>"; //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* @return true if fileName is a type of file that may affect a server's classpath.
*/
public static boolean canAffectClasspath(String fileName) {
if (fileName == null || fileName.length() == 0) {
return false;
}
return ".classpath".equals(fileName) || EGLProject.EGLPATH_FILENAME.equals(fileName) //$NON-NLS-1$
|| IRUtils.matchesFileName(fileName, SUFFIX_egldd, SUFFIX_EGLDD);
}
/**
* Verifies that the classpath entry is valid and won't cause the launch to fail.
*
* @param entry The entry to validate.
* @param serverProject The project that the test server will run out of.
* @return true if it's valid
*/
public static boolean classpathEntryIsValid(String entry, IProject serverProject) {
try {
ILaunchConfigurationType type = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType(
IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfigurationWorkingCopy copy = type.newInstance(null, "ezeTemp"); //$NON-NLS-1$
copy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, serverProject.getName());
copy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
List<String> classpath = new ArrayList<String>(1);
classpath.add(entry);
copy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, classpath);
JavaRuntime.resolveRuntimeClasspath(JavaRuntime.computeUnresolvedRuntimeClasspath(copy), copy);
return true;
}
catch (CoreException ce) {
return false;
}
}
/**
* @return the resolved classpath for the launch configuration. All entries, not just user entries, are returned.
* @throws CoreException
*/
public static String[] resolveClasspath(ILaunchConfiguration config) throws CoreException {
IRuntimeClasspathEntry[] entries = JavaRuntime.resolveRuntimeClasspath(JavaRuntime.computeUnresolvedRuntimeClasspath(config), config);
String[] paths = new String[entries.length];
for (int i = 0; i < entries.length; i++) {
paths[i] = entries[i].getLocation();
}
return paths;
}
}