/******************************************************************************* * Copyright (c) 2009, 2012 SpringSource, a divison of VMware, Inc. * 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: * SpringSource, a division of VMware, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.virgo.ide.jdt.internal.core.util; import java.io.File; import java.util.Arrays; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.IEclipsePreferences; 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.core.JavaModelException; import org.eclipse.virgo.ide.facet.core.FacetUtils; import org.eclipse.virgo.ide.jdt.core.JdtCorePlugin; import org.eclipse.virgo.ide.jdt.internal.core.classpath.ServerClasspathContainer; import org.eclipse.virgo.ide.jdt.internal.core.classpath.ServerClasspathContainerUpdateJob; import org.eclipse.virgo.ide.manifest.core.BundleManifestCorePlugin; import org.eclipse.virgo.ide.manifest.core.BundleManifestUtils; import org.eclipse.virgo.ide.manifest.core.IBundleManifestChangeListener; import org.eclipse.virgo.ide.manifest.internal.core.BundleManifestManager; import org.eclipse.virgo.util.osgi.manifest.BundleManifest; import org.eclipse.virgo.util.osgi.manifest.ImportedPackage; import org.eclipse.virgo.util.osgi.manifest.RequiredBundle; /** * Helper methods to be used to within the class path container infrastructure. * * @author Christian Dupuis * @since 1.0.0 */ public class ClasspathUtils { /** * Adjusts the last modified timestamp on the source root and META-INF directory to reflect the same date as the * MANIFEST.MF. * <p> * Note: this is a requirement for the dependency resolution. */ public static void adjustLastModifiedDate(IJavaProject javaProject, boolean testBundle) { File manifest = BundleManifestUtils.locateManifestFile(javaProject, testBundle); if (manifest != null && manifest.canRead()) { long lastmodified = manifest.lastModified(); File metaInfFolder = manifest.getParentFile(); if (metaInfFolder != null && metaInfFolder.getName().equals(BundleManifestCorePlugin.MANIFEST_FOLDER_NAME) && metaInfFolder.canWrite()) { metaInfFolder.setLastModified(lastmodified); File srcFolder = metaInfFolder.getParentFile(); if (srcFolder != null && srcFolder.canWrite()) { srcFolder.setLastModified(lastmodified); } } } } /** * Returns <code>true</code> if the given project has the bundle dependency classpath container installed. */ public static boolean hasClasspathContainer(IJavaProject javaProject) { boolean hasContainer = false; try { for (IClasspathEntry entry : javaProject.getRawClasspath()) { if (entry.getPath().equals(ServerClasspathContainer.CLASSPATH_CONTAINER_PATH)) { hasContainer = true; break; } } } catch (JavaModelException e) { JdtCorePlugin.log(e); } return hasContainer; } /** * Returns the {@link ServerClasspathContainer} for the given <code>javaProject</code>. * <p> * This method returns <code>null</code> if no appropriate class path container could be found on the given project. */ public static ServerClasspathContainer getClasspathContainer(IJavaProject javaProject) { try { if (hasClasspathContainer(javaProject)) { IClasspathContainer container = JavaCore.getClasspathContainer(ServerClasspathContainer.CLASSPATH_CONTAINER_PATH, javaProject); if (container instanceof ServerClasspathContainer) { return (ServerClasspathContainer) container; } } } catch (JavaModelException e) { JdtCorePlugin.log(e); } return null; } /** * Returns configured source attachment paths for a given jar resource path. * * @param project the java project which preferences needs to be checked. * @param file the jar that needs a source attachment */ public static IPath getSourceAttachment(IJavaProject project, File file) { IEclipsePreferences preferences = JdtCorePlugin.getDefault().getProjectPreferences(project.getProject()); String value = preferences.get("source.attachment-" + file.getName(), null); if (value != null) { return new Path(value); } return null; } /** * Stores the configured source attachments paths in the projects settings area. * * @param project the java project to store the preferences for * @param containerSuggestion the configured classpath container entries */ public static void storeSourceAttachments(IJavaProject project, IClasspathContainer containerSuggestion) { IEclipsePreferences preferences = JdtCorePlugin.getDefault().getProjectPreferences(project.getProject()); for (IClasspathEntry entry : containerSuggestion.getClasspathEntries()) { IPath path = entry.getPath(); IPath sourcePath = entry.getSourceAttachmentPath(); if (sourcePath != null) { preferences.put("source.attachment-" + path.lastSegment().toString(), sourcePath.toString()); } } } /** * Updates the class path container on the given <code>javaProject</code>. */ public static IStatus updateClasspathContainer(IJavaProject javaProject, Set<IBundleManifestChangeListener.Type> types, IProgressMonitor monitor) { try { if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } // only update if something relevant happened if (types.contains(IBundleManifestChangeListener.Type.IMPORT_BUNDLE) || types.contains(IBundleManifestChangeListener.Type.IMPORT_LIBRARY) || types.contains(IBundleManifestChangeListener.Type.IMPORT_PACKAGE) || types.contains(IBundleManifestChangeListener.Type.REQUIRE_BUNDLE)) { IClasspathContainer oldContainer = ClasspathUtils.getClasspathContainer(javaProject); if (oldContainer != null) { ServerClasspathContainer container = new ServerClasspathContainer(javaProject); container.refreshClasspathEntries(); if (!Arrays.deepEquals(oldContainer.getClasspathEntries(), container.getClasspathEntries())) { JavaCore.setClasspathContainer(ServerClasspathContainer.CLASSPATH_CONTAINER_PATH, new IJavaProject[] { javaProject }, new IClasspathContainer[] { container }, monitor); } } } if (types.contains(IBundleManifestChangeListener.Type.EXPORT_PACKAGE)) { // Always try to update the depending projects because MANIFEST might have changed // without changes to the project's own class path updateClasspathContainerForDependingBundles(javaProject); } } catch (JavaModelException e) { JdtCorePlugin.log(e); } return Status.OK_STATUS; } /** * Triggers an class path container update on depending {@link IJavaProject}s. */ private static void updateClasspathContainerForDependingBundles(IJavaProject javaProject) { BundleManifest updatedBundleManifest = BundleManifestCorePlugin.getBundleManifestManager().getBundleManifest(javaProject); Set<String> updatedPackageExports = BundleManifestCorePlugin.getBundleManifestManager().getPackageExports(javaProject); if (updatedBundleManifest != null && updatedBundleManifest.getBundleSymbolicName() != null) { String bundleSymbolicName = updatedBundleManifest.getBundleSymbolicName().getSymbolicName(); for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { try { // Only dm server bundle projects are of interest if (!javaProject.getProject().equals(project) && FacetUtils.isBundleProject(project)) { IJavaProject jp = JavaCore.create(project); BundleManifest manifest = BundleManifestCorePlugin.getBundleManifestManager().getBundleManifest(jp); boolean refreshClasspath = false; if (manifest != null) { // Check for Require-Bundle dependency if (manifest.getRequireBundle() != null) { for (RequiredBundle requiredBundle : manifest.getRequireBundle().getRequiredBundles()) { if (bundleSymbolicName != null && bundleSymbolicName.equals(requiredBundle.getBundleSymbolicName())) { refreshClasspath = true; break; } } } // Check for export -> import package dependency if (manifest.getImportPackage() != null) { for (ImportedPackage packageImport : manifest.getImportPackage().getImportedPackages()) { if (updatedPackageExports.contains(packageImport.getPackageName())) { refreshClasspath = true; break; } } } // Check for explicit project dependencies for (String requiredProjectName : jp.getRequiredProjectNames()) { if (requiredProjectName.equals(javaProject.getElementName())) { refreshClasspath = true; break; } } // Schedule class path container update if (refreshClasspath) { ServerClasspathContainerUpdateJob.scheduleClasspathContainerUpdateJob(jp, BundleManifestManager.IMPORTS_CHANGED); } } } } catch (Exception e) { JdtCorePlugin.log(e); } } } } }