org.jboss.tools.maven.jsf.configurators.JSFProjectConfigurator.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.tools.maven.jsf.configurators.JSFProjectConfigurator.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is 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:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package org.jboss.tools.maven.jsf.configurators;

import static org.jboss.tools.maven.jsf.configurators.JSFUtils.JSF_VERSION_1_1;
import static org.jboss.tools.maven.jsf.configurators.JSFUtils.JSF_VERSION_1_2;
import static org.jboss.tools.maven.jsf.configurators.JSFUtils.JSF_VERSION_2_0;
import static org.jboss.tools.maven.jsf.configurators.JSFUtils.JSF_VERSION_2_1;

import java.util.List;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
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.jface.preference.IPreferenceStore;
import org.eclipse.jst.j2ee.project.facet.IJ2EEFacetConstants;
import org.eclipse.jst.jsf.core.internal.project.facet.IJSFFacetInstallDataModelProperties;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.MavenProjectChangedEvent;
import org.eclipse.m2e.core.project.configurator.AbstractProjectConfigurator;
import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest;
import org.eclipse.wst.common.componentcore.ModuleCoreNature;
import org.eclipse.wst.common.frameworks.datamodel.IDataModel;
import org.eclipse.wst.common.project.facet.core.IFacetedProject;
import org.eclipse.wst.common.project.facet.core.IProjectFacet;
import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
import org.jboss.tools.maven.core.IJBossMavenConstants;
import org.jboss.tools.maven.core.MavenUtil;
import org.jboss.tools.maven.core.ProjectUtil;
import org.jboss.tools.maven.core.internal.project.facet.MavenFacetInstallDataModelProvider;
import org.jboss.tools.maven.jsf.MavenJSFActivator;
import org.jboss.tools.maven.jsf.MavenJSFConstants;
import org.jboss.tools.maven.jsf.Messages;
import org.jboss.tools.maven.ui.Activator;

/**
 * 
 * @author snjeza
 *
 */
public class JSFProjectConfigurator extends AbstractProjectConfigurator {

    private static final String JSF_API_GROUP_ID = "javax.faces"; //$NON-NLS-1$
    private static final String JSF_API2_GROUP_ID = "com.sun.faces"; //$NON-NLS-1$
    private static final String JSF_API_ARTIFACT_ID = "jsf-api"; //$NON-NLS-1$

    private static final String WAR_SOURCE_FOLDER = "/src/main/webapp";

    public static final IProjectFacet JSF_FACET;
    public static final IProjectFacetVersion JSF_FACET_VERSION_2_1;
    public static final IProjectFacetVersion JSF_FACET_VERSION_2_0;
    public static final IProjectFacetVersion JSF_FACET_VERSION_1_2;
    public static final IProjectFacetVersion JSF_FACET_VERSION_1_1;
    protected static final IProjectFacet m2Facet;
    protected static final IProjectFacetVersion m2Version;

    static {
        JSF_FACET = ProjectFacetsManager.getProjectFacet("jst.jsf"); //$NON-NLS-1$
        JSF_FACET_VERSION_2_0 = JSF_FACET.getVersion(JSF_VERSION_2_0);
        JSF_FACET_VERSION_1_2 = JSF_FACET.getVersion(JSF_VERSION_1_2); //$NON-NLS-1$
        JSF_FACET_VERSION_1_1 = JSF_FACET.getVersion(JSF_VERSION_1_1); //$NON-NLS-1$

        IProjectFacetVersion jsf21Version = null;
        try {
            jsf21Version = JSF_FACET.getVersion(JSF_VERSION_2_1);
        } catch (Exception e) {
            MavenJSFActivator.warn("JSF 2.1 Facet is unavailable, fall back to 2.0");
            jsf21Version = JSF_FACET_VERSION_2_0;
        }
        JSF_FACET_VERSION_2_1 = jsf21Version;

        m2Facet = ProjectFacetsManager.getProjectFacet("jboss.m2"); //$NON-NLS-1$
        m2Version = m2Facet.getVersion("1.0"); //$NON-NLS-1$
    }

    @Override
    public void configure(ProjectConfigurationRequest request, IProgressMonitor monitor) throws CoreException {
        MavenProject mavenProject = request.getMavenProject();
        IProject project = request.getProject();
        configureInternal(mavenProject, project, monitor);
    }

    private void configureInternal(MavenProject mavenProject, IProject project, IProgressMonitor monitor)
            throws CoreException {

        if (!"war".equals(mavenProject.getPackaging())) {//$NON-NLS-1$
            return;
        }

        IPreferenceStore store = Activator.getDefault().getPreferenceStore();
        boolean configureJSF = store.getBoolean(Activator.CONFIGURE_JSF);
        if (!configureJSF) {
            return;
        }

        final IFacetedProject fproj = ProjectFacetsManager.create(project);
        if (fproj != null && fproj.hasProjectFacet(JSF_FACET) && fproj.hasProjectFacet(m2Facet)) {
            //everything already installed. Since there's no support for version update -yet- we bail
            return;
        }

        String jsfVersion = getJSFVersion(mavenProject, fproj);
        if (fproj != null && jsfVersion != null) {
            installWarFacets(fproj, jsfVersion, mavenProject, monitor);
        }
    }

    @Override
    public void mavenProjectChanged(MavenProjectChangedEvent event, IProgressMonitor monitor) throws CoreException {
        IMavenProjectFacade facade = event.getMavenProject();
        if (facade != null) {
            IProject project = facade.getProject();
            if (isWTPProject(project)) {
                MavenProject mavenProject = facade.getMavenProject(monitor);

                IMavenProjectFacade oldFacade = event.getOldMavenProject();
                if (oldFacade != null) {
                    MavenProject oldProject = oldFacade.getMavenProject(monitor);
                    if (oldProject != null && oldProject.getArtifacts().equals(mavenProject.getArtifacts())) {
                        //Nothing changed since last build, no need to lookup for new JSF facets
                        return;
                    }
                }
                configureInternal(mavenProject, project, monitor);
            }
        }
        super.mavenProjectChanged(event, monitor);
    }

    private boolean isWTPProject(IProject project) {
        return ModuleCoreNature.getModuleCoreNature(project) != null;
    }

    private void installM2Facet(IFacetedProject fproj, IProgressMonitor monitor) throws CoreException {
        if (!fproj.hasProjectFacet(m2Facet)) {
            IDataModel config = (IDataModel) new MavenFacetInstallDataModelProvider().create();
            config.setBooleanProperty(IJBossMavenConstants.MAVEN_PROJECT_EXISTS, true);
            fproj.installProjectFacet(m2Version, config, monitor);
        }
    }

    private void installWarFacets(IFacetedProject fproj, String jsfVersion, MavenProject mavenProject,
            IProgressMonitor monitor) throws CoreException {

        if (!fproj.hasProjectFacet(IJ2EEFacetConstants.DYNAMIC_WEB_FACET)) {
            Activator.log(Messages.JSFProjectConfigurator_The_project_does_not_contain_the_Web_Module_facet);
        } else {
            installJSFFacet(fproj, jsfVersion, mavenProject, monitor);
        }
        installM2Facet(fproj, monitor);

    }

    private void installJSFFacet(IFacetedProject fproj, String jsfVersionString, MavenProject mavenProject,
            IProgressMonitor monitor) throws CoreException {

        markerManager.deleteMarkers(fproj.getProject(), MavenJSFConstants.JSF_CONFIGURATION_ERROR_MARKER_ID);
        if (!fproj.hasProjectFacet(JSF_FACET)) {
            IProject project = fproj.getProject();
            //JBIDE-10785 : refresh parent to prevent 
            // org.osgi.service.prefs.BackingStoreException: Resource '/parent/web/.settings' does not exist.
            MavenUtil.refreshParent(mavenProject);

            String warSourceDir = getWarSourceDirectory(mavenProject, project);
            String facesConfigPath = "WEB-INF/faces-config.xml";
            IFile defaultFacesConfig = project.getFolder(warSourceDir).getFile(facesConfigPath);
            IFile generatedFacesConfig = ProjectUtil.getGeneratedWebResourceFile(project, facesConfigPath);
            IFile actualFacesConfig = JSFUtils.getFacesconfig(project);
            IFolder libFolder = project.getFolder(warSourceDir).getFolder("WEB-INF/lib");

            //faces-config.xml will not be created in the source folder and it doesn't exist yet
            // => We'll have to fix it after setting the JSF facet
            boolean shouldFixFacesConfig = generatedFacesConfig != null
                    && !generatedFacesConfig.getLocation().equals(defaultFacesConfig.getLocation())
                    && !generatedFacesConfig.exists();
            boolean defaultFacesConfigAlreadyExists = defaultFacesConfig.exists();
            boolean libFolderAlreadyExists = libFolder.exists();

            IProjectFacetVersion facetVersion = null;
            boolean configureServlet = false;//Fix for JBIDE-9454, where existing web.xml is completely overwritten.
            if (jsfVersionString.startsWith(JSF_VERSION_1_1)) {
                facetVersion = JSF_FACET_VERSION_1_1;
            } else if (jsfVersionString.startsWith(JSF_VERSION_1_2)) {
                facetVersion = JSF_FACET_VERSION_1_2;
            } else if (jsfVersionString.startsWith(JSF_VERSION_2_0)) {
                facetVersion = JSF_FACET_VERSION_2_0;
            } else if (jsfVersionString.startsWith(JSF_VERSION_2_1)) {
                facetVersion = JSF_FACET_VERSION_2_1;
            }

            if (facetVersion != null) {
                IStatus status = facetVersion.getConstraint().check(fproj.getProjectFacets());
                if (status.isOK()) {
                    IDataModel model = MavenJSFActivator.getDefault().createJSFDataModel(fproj, facetVersion);
                    model.setBooleanProperty(IJSFFacetInstallDataModelProperties.CONFIGURE_SERVLET,
                            configureServlet);
                    fproj.installProjectFacet(facetVersion, model, monitor);
                } else {
                    addErrorMarker(fproj.getProject(),
                            facetVersion + " can not be installed : " + status.getMessage());
                    for (IStatus st : status.getChildren()) {
                        addErrorMarker(fproj.getProject(), st.getMessage());
                    }
                }
            }

            if (shouldFixFacesConfig && generatedFacesConfig.exists()) {
                if (defaultFacesConfig.exists()) {
                    //We have 2 config files. Delete the gen'd one
                    generatedFacesConfig.delete(true, monitor);
                } else {
                    //move the gen'd config file to the appropriate source folder
                    IContainer destination = defaultFacesConfig.getParent();
                    if (destination != null && !destination.exists()) {
                        destination.getLocation().toFile().mkdirs();
                    }
                    generatedFacesConfig.move(defaultFacesConfig.getFullPath(), true, monitor);
                }
            }
            if (actualFacesConfig != null && !defaultFacesConfigAlreadyExists
                    && !defaultFacesConfig.getLocation().equals(actualFacesConfig.getLocation())
                    && defaultFacesConfig.exists()/*file has just been created*/) {
                defaultFacesConfig.delete(true, monitor);
            }
            //JBIDE-11413 : don't create an unnecessary lib folder
            libFolder.refreshLocal(IResource.DEPTH_ZERO, monitor);
            if (!libFolderAlreadyExists && libFolder.exists() && libFolder.members().length == 0) {
                libFolder.delete(true, monitor);
            }
        }
    }

    private void addErrorMarker(IProject project, String message) {
        markerManager.addMarker(project, MavenJSFConstants.JSF_CONFIGURATION_ERROR_MARKER_ID, message, -1,
                IMarker.SEVERITY_ERROR);

    }

    private String getWarSourceDirectory(MavenProject mavenProject, IProject project) {
        Plugin plugin = mavenProject.getPlugin("org.apache.maven.plugins:maven-war-plugin");
        if (plugin == null) {
            return null;
        }
        Xpp3Dom config = (Xpp3Dom) plugin.getConfiguration();
        if (config == null) {
            return WAR_SOURCE_FOLDER;
        }
        Xpp3Dom[] warSourceDirectory = config.getChildren("warSourceDirectory");
        if (warSourceDirectory != null && warSourceDirectory.length > 0) {
            String dir = warSourceDirectory[0].getValue();
            if (project != null) {
                return tryProjectRelativePath(project, dir).toOSString();
            }
            return dir;
        }
        return WAR_SOURCE_FOLDER;
    }

    private IPath tryProjectRelativePath(IProject project, String resourceLocation) {
        if (resourceLocation == null) {
            return null;
        }
        IPath projectLocation = project.getLocation();
        IPath directory = Path.fromOSString(resourceLocation);
        if (projectLocation == null || !projectLocation.isPrefixOf(directory)) {
            return directory;
        }
        return directory.removeFirstSegments(projectLocation.segmentCount()).makeRelative().setDevice(null);
    }

    /*
    private boolean configureWebxml() {
       IPreferenceStore store = Activator.getDefault().getPreferenceStore();
       return store.getBoolean(Activator.CONFIGURE_WEBXML_JSF20);
    }
    */

    private String getJSFVersion(MavenProject mavenProject, IFacetedProject fproj) {
        String version = null;
        IProject project = fproj.getProject();
        version = JSFUtils.getVersionFromFacesconfig(project);

        if (version == null) {
            version = Activator.getDefault().getDependencyVersion(mavenProject, JSF_API_GROUP_ID,
                    JSF_API_ARTIFACT_ID);
        }
        if (version == null) {
            //Check if there's a JSF 2.x dependency 
            version = Activator.getDefault().getDependencyVersion(mavenProject, JSF_API2_GROUP_ID,
                    JSF_API_ARTIFACT_ID);
        }
        if (version == null) {
            //JBIDE-9242 determine JSF version from classpath  
            version = JSFUtils.getJSFVersionFromClasspath(project);
        }
        if (version == null) {
            version = inferJsfVersionFromDependencies(mavenProject, JSF_API2_GROUP_ID, JSF_API_ARTIFACT_ID,
                    JSF_VERSION_2_0);
        }

        if (version == null && hasFacesServletInWebXml(mavenProject, project)) {
            //No dependency on JSF, no faces-config, but uses faces-servlet
            //so we try to best guess the version depending on the installed web facet
            IProjectFacetVersion webVersion = fproj.getInstalledVersion(IJ2EEFacetConstants.DYNAMIC_WEB_FACET);
            if (webVersion.compareTo(IJ2EEFacetConstants.DYNAMIC_WEB_30) < 0) {
                version = JSF_VERSION_1_2;
            } else {
                version = JSF_VERSION_2_0;
            }
        }

        return version;
    }

    private boolean hasFacesServletInWebXml(MavenProject mavenProject, IProject project) {
        //We look for javax.faces.webapp.FacesServlet in web.xml
        //We should look for a custom web.xml at this point, but WTP would then crash on the JSF Facet installation
        //if it's not in a standard location, so we stick with the regular file.
        IFile webXml = getWebXml(project);
        return webXml != null && webXml.exists() && JSFUtils.hasFacesServlet(webXml);
    }

    private IFile getWebXml(IProject project) {
        return ProjectUtil.getWebResourceFile(project, "WEB-INF/web.xml");
    }

    private String inferJsfVersionFromDependencies(MavenProject mavenProject, String groupId, String artifactId,
            String defaultVersion) {
        boolean hasCandidates = false;
        String jsfVersion = null;
        List<ArtifactRepository> repos = mavenProject.getRemoteArtifactRepositories();
        for (Artifact artifact : mavenProject.getArtifacts()) {
            if (isKnownJsfBasedArtifact(artifact)) {
                hasCandidates = true;
                jsfVersion = Activator.getDefault().getDependencyVersion(artifact, repos, groupId, artifactId);
                if (jsfVersion != null) {
                    //TODO should probably not break and take the highest version returned from all dependencies
                    break;
                }
            }
        }
        //Fallback to default JSF version
        if (hasCandidates && jsfVersion == null) {
            return defaultVersion;
        }
        return jsfVersion;
    }

    private boolean isKnownJsfBasedArtifact(Artifact artifact) {
        return artifact.getGroupId().startsWith("org.jboss.seam.") //$NON-NLS-1$ 
                && artifact.getArtifactId().equals("seam-faces") //$NON-NLS-1$
                && artifact.getVersion().startsWith("3."); //$NON-NLS-1$
    }

}