Package org.sonatype.tycho.m2e.internal

Source Code of org.sonatype.tycho.m2e.internal.PDEProjectHelper$UpdateClasspathWorkspaceJob

/*******************************************************************************
* Copyright (c) 2011 Sonatype, 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
*******************************************************************************/
package org.sonatype.tycho.m2e.internal;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.maven.project.MavenProject;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
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.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.m2e.core.project.MavenProjectUtils;
import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.core.project.IBundleProjectService;
import org.eclipse.pde.internal.core.ClasspathComputer;
import org.eclipse.pde.internal.core.IPluginModelListener;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.PluginModelDelta;
import org.eclipse.pde.internal.core.natures.PDE;
import org.eclipse.pde.internal.core.project.PDEProject;
import org.eclipse.pde.internal.core.util.CoreUtility;

@SuppressWarnings( "restriction" )
public class PDEProjectHelper
{
    private static boolean isListeningForPluginModelChanges = false;

    private static final List<IProject> projectsForUpdateClasspath = new ArrayList<IProject>();

    private static final PDEProjectHelper INSTANCE = new PDEProjectHelper();

    private PDEProjectHelper()
    {

    }

    public static PDEProjectHelper getInstance()
    {
        return INSTANCE;
    }

    private static final IPluginModelListener classpathUpdater = new IPluginModelListener()
    {
        public void modelsChanged( PluginModelDelta delta )
        {
            synchronized ( projectsForUpdateClasspath )
            {
                if ( projectsForUpdateClasspath.size() == 0 )
                {
                    return;
                }

                Iterator<IProject> projectsIter = projectsForUpdateClasspath.iterator();
                while ( projectsIter.hasNext() )
                {
                    IProject project = projectsIter.next();
                    IPluginModelBase model = PluginRegistry.findModel( project );
                    if ( model == null )
                    {
                        continue;
                    }

                    UpdateClasspathWorkspaceJob job = new UpdateClasspathWorkspaceJob( project, model );
                    job.schedule();
                    projectsIter.remove();
                }
            }
        }
    };

    private static class UpdateClasspathWorkspaceJob
        extends WorkspaceJob
    {
        private final IProject project;

        private final IPluginModelBase model;

        public UpdateClasspathWorkspaceJob( IProject project, IPluginModelBase model )
        {
            super( "Updating classpath" );
            this.project = project;
            this.model = model;
        }

        @Override
        public IStatus runInWorkspace( IProgressMonitor monitor )
            throws CoreException
        {
            setClasspath( project, model, monitor );
            return Status.OK_STATUS;
        }
    }

    public void configurePDEBundleProject( IProject project, MavenProject mavenProject, IProgressMonitor monitor )
        throws CoreException
    {
        // see org.eclipse.pde.internal.ui.wizards.plugin.NewProjectCreationOperation

        if ( !project.hasNature( PDE.PLUGIN_NATURE ) )
        {
            CoreUtility.addNatureToProject( project, PDE.PLUGIN_NATURE, null );
        }

        if ( !project.hasNature( JavaCore.NATURE_ID ) )
        {
            CoreUtility.addNatureToProject( project, JavaCore.NATURE_ID, null );
        }

        // PDE can't handle default JDT classpath
        IJavaProject javaProject = JavaCore.create( project );
        javaProject.setOutputLocation( getOutputLocation( project, mavenProject, monitor ), monitor );

        // see org.eclipse.pde.internal.ui.wizards.tools.UpdateClasspathJob
        // PDE populates the model cache lazily from WorkspacePluginModelManager.visit() ResourceChangeListenter
        // That means the model may be available or not at this point in the lifecycle.
        // If it is, update its classpath right away.
        // If not add the project to the list to be updated later based on model change events.
        IPluginModelBase model = PluginRegistry.findModel( project );
        if ( model != null )
        {
            setClasspath( project, model, monitor );
        }
        else
        {
            addProjectForUpdateClasspath( project );
        }
    }

    private void addProjectForUpdateClasspath( IProject project )
    {
        synchronized ( projectsForUpdateClasspath )
        {
            projectsForUpdateClasspath.add( project );
            if ( !isListeningForPluginModelChanges )
            {
                PDECore.getDefault().getModelManager().addPluginModelListener( classpathUpdater );
                isListeningForPluginModelChanges = true;
            }
        }
    }

    private IPath getOutputLocation( IProject project, MavenProject mavenProject, IProgressMonitor monitor )
        throws CoreException
    {
        File outputDirectory = new File( mavenProject.getBuild().getOutputDirectory() );
        outputDirectory.mkdirs();
        IPath relPath =
            MavenProjectUtils.getProjectRelativePath( project, mavenProject.getBuild().getOutputDirectory() );
        IFolder folder = project.getFolder( relPath );
        folder.refreshLocal( IResource.DEPTH_INFINITE, monitor );
        return folder.getFullPath();
    }

    public static void addPDENature( IProject project, IPath manifestPath, IProgressMonitor monitor )
        throws CoreException
    {
        AbstractProjectConfigurator.addNature( project, PDE.PLUGIN_NATURE, monitor );
        IProjectDescription description = project.getDescription();
        ICommand[] prevBuilders = description.getBuildSpec();
        ArrayList<ICommand> newBuilders = new ArrayList<ICommand>();
        for ( ICommand builder : prevBuilders )
        {
            if ( !builder.getBuilderName().startsWith( "org.eclipse.pde" ) )
            {
                newBuilders.add( builder );
            }
        }
        description.setBuildSpec( newBuilders.toArray( new ICommand[newBuilders.size()] ) );
        project.setDescription( description, monitor );

        setManifestLocaton( project, manifestPath, monitor );
    }

    protected static void setManifestLocaton( IProject project, IPath manifestPath, IProgressMonitor monitor )
        throws CoreException
    {
        IBundleProjectService projectService = M2ETychoActivator.getDefault().getProjectService();
        if ( manifestPath != null && manifestPath.segmentCount() > 1 )
        {
            IPath metainfPath = manifestPath.removeLastSegments( 1 );
            project.getFile( metainfPath ).refreshLocal( IResource.DEPTH_INFINITE, monitor );
            projectService.setBundleRoot( project, metainfPath );
        }
        else
        {
            // in case of configuration update, reset to the default value
            projectService.setBundleRoot( project, null );
        }
    }

    /**
     * Returns bundle manifest as known to PDE project metadata. Returned file may not exist in the workspace or on the
     * filesystem. Never returns null.
     */
    public static IFile getBundleManifest( IProject project )
        throws CoreException
    {
        // PDE API is very inconvenient, lets use internal classes instead
        IContainer metainf = PDEProject.getBundleRoot( project );
        if ( metainf == null || metainf instanceof IProject )
        {
            metainf = project.getFolder( "META-INF" );
        }
        else
        {
            metainf = metainf.getFolder( new Path( "META-INF" ) );
        }

        return metainf.getFile( new Path( "MANIFEST.MF" ) );
    }

    private static void setClasspath( IProject project, IPluginModelBase model, IProgressMonitor monitor )
        throws CoreException
    {
        IClasspathEntry[] entries = ClasspathComputer.getClasspath(project, model, null, true /*clear existing entries*/, true);
        JavaCore.create(project).setRawClasspath(entries, null);
        // workaround PDE sloppy model management during the first multimodule project import in eclipse session
        // 1. m2e creates all modules as simple workspace projects without JDT or PDE natures
        // 2. first call to org.eclipse.pde.internal.core.PluginModelManager.initializeTable() reads all workspace
        // projects regardless of their natures (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=319268)
        // 3. going through all projects one by one
        // 3.1. m2e enables JDE and PDE natures and adds PDE classpath container
        // 3.2. org.eclipse.pde.internal.core.PDEClasspathContainer.addProjectEntry ignores all project's dependencies
        // that do not have JAVA nature. at this point PDE classpath is missing some/all workspace dependencies.
        // 4. PDE does not re-resolve classpath when dependencies get JAVA nature enabled

        // as a workaround, touch project bundle manifests to force PDE re-read the model, re-resolve dependencies
        // and recalculate PDE classpath

        IFile manifest = PDEProjectHelper.getBundleManifest( project );
        if ( manifest.isAccessible() )
        {
            manifest.touch( monitor );
        }
    }
}
TOP

Related Classes of org.sonatype.tycho.m2e.internal.PDEProjectHelper$UpdateClasspathWorkspaceJob

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.