org.eclipse.jst.j2ee.internal.classpathdep.ClasspathDependencyValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jst.j2ee.internal.classpathdep.ClasspathDependencyValidator.java

Source

/*******************************************************************************
 * Copyright (c) 2006 2007 BEA Systems, Inc. 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:
 * BEA Systems, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.jst.j2ee.internal.classpathdep;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IContainer;
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.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jst.common.jdt.internal.javalite.IJavaProjectLite;
import org.eclipse.jst.common.jdt.internal.javalite.JavaCoreLite;
import org.eclipse.jst.common.jdt.internal.javalite.JavaLiteUtilities;
import org.eclipse.jst.j2ee.classpathdep.ClasspathDependencyUtil;
import org.eclipse.jst.j2ee.classpathdep.IClasspathDependencyConstants;
import org.eclipse.jst.j2ee.classpathdep.IClasspathDependencyConstants.DependencyAttributeType;
import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin;
import org.eclipse.jst.j2ee.project.JavaEEProjectUtilities;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.ModuleCoreNature;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.project.facet.core.FacetedProjectFramework;
import org.eclipse.wst.validation.internal.core.Message;
import org.eclipse.wst.validation.internal.core.ValidationException;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
import org.eclipse.wst.validation.internal.provisional.core.IValidatorJob;

/**
 * Validates classpath entries that have been tagged as component dependencies.
 */
public class ClasspathDependencyValidator implements IValidatorJob {

    public static final String AppClientProject = "AppClientProject"; //$NON-NLS-1$
    public static final String RootMappingNonEARWARRef = "RootMappingNonEARWARRef"; //$NON-NLS-1$
    public static final String NonTaggedExportedClasses = "NonTaggedExportedClasses"; //$NON-NLS-1$
    public static final String DuplicateArchiveName = "DuplicateArchiveName"; //$NON-NLS-1$
    public static final String ProjectClasspathEntry = "ProjectClasspathEntry"; //$NON-NLS-1$
    public static final String SourceEntry = "SourceEntry"; //$NON-NLS-1$
    public static final String FilteredContainer = "FilteredContainer"; //$NON-NLS-1$
    public static final String DuplicateClassFolderEntry = "DuplicateClassFolderEntry"; //$NON-NLS-1$
    public static final String NonWebNonExported = "NonWebNonExported"; //$NON-NLS-1$
    public static final String InvalidNonWebRuntimePath = "InvalidNonWebRuntimePath"; //$NON-NLS-1$
    public static final String InvalidWebRuntimePath = "InvalidWebRuntimePath"; //$NON-NLS-1$

    protected IReporter _reporter;

    public ClasspathDependencyValidator() {
        super();
    }

    public IStatus validateInJob(IValidationContext helper, IReporter reporter) throws ValidationException {
        _reporter = reporter;
        //Remove all markers related to this validator
        _reporter.removeAllMessages(this);
        //Using the helper class, load the module model
        final Set archiveNames = new HashSet();
        final IProject proj = ((ClasspathDependencyValidatorHelper) helper).getProject();

        try {
            if (ModuleCoreNature.isFlexibleProject(proj) && proj.hasNature(JavaCoreLite.NATURE_ID)) {

                final boolean isWebApp = JavaEEProjectUtilities.isDynamicWebProject(proj);
                final IVirtualComponent component = ComponentCore.createComponent(proj);
                final boolean isLegacyJ2EE = JavaEEProjectUtilities.isLegacyJ2EEComponent(component);

                final IJavaProjectLite javaProjectLite = JavaCoreLite.create(proj);
                final Map referencedRawEntries = ClasspathDependencyUtil.getRawComponentClasspathDependencies(
                        javaProjectLite, DependencyAttributeType.CLASSPATH_COMPONENT_DEPENDENCY, isLegacyJ2EE);
                final List potentialRawEntries = ClasspathDependencyUtil
                        .getPotentialComponentClasspathDependencies(javaProjectLite, isLegacyJ2EE);
                final ClasspathDependencyValidatorData data = new ClasspathDependencyValidatorData(proj);

                ClasspathDependencyExtensionManager extensionManger = ClasspathDependencyExtensionManager
                        .instance();

                // validate the raw referenced container entries
                Iterator i = referencedRawEntries.keySet().iterator();
                boolean hasRootMapping = false;
                while (i.hasNext()) {
                    final IClasspathEntry entry = (IClasspathEntry) i.next();
                    final boolean isClassFolder = ClasspathDependencyUtil.isClassFolderEntry(entry);
                    final IClasspathAttribute attrib = (IClasspathAttribute) referencedRawEntries.get(entry);
                    final IPath runtimePath = ClasspathDependencyUtil.getRuntimePath(attrib, isWebApp,
                            isClassFolder);
                    if (runtimePath.equals(IClasspathDependencyConstants.RUNTIME_MAPPING_INTO_CONTAINER_PATH)) {
                        hasRootMapping = true;
                    }
                    IMessage[] msgs = validateVirtualComponentEntry(entry, attrib, isWebApp, proj, data);
                    final String cpEntryPath = entry.getPath().toString();
                    for (int j = 0; j < msgs.length; j++) {
                        msgs[j].setGroupName(cpEntryPath);
                    }
                    reportMessages(msgs);
                    // if not a web app, warn if associated cp entry is not exported
                    if (!isWebApp && !entry.isExported() && !extensionManger.doesProjectHandleExport(proj, entry)) {
                        _reporter.addMessage(this, new Message("classpathdependencyvalidator", //$NON-NLS-1$
                                IMessage.NORMAL_SEVERITY, NonWebNonExported, new String[] { cpEntryPath }, proj));
                    }
                }

                if (!referencedRawEntries.isEmpty()) {
                    //               if (JavaEEProjectUtilities.isApplicationClientProject(proj)) { 
                    //                  // classpath component dependencies are not supported for application client projects
                    //                  final IMessage msg = new Message("classpathdependencyvalidator", //$NON-NLS-1$
                    //                        IMessage.HIGH_SEVERITY, AppClientProject, null, proj);
                    //                  _reporter.addMessage(this, msg);
                    //               }

                    // are there any root mappings
                    if (hasRootMapping && component != null) {
                        boolean referencedFromEARorWAR = false;
                        final List earWarRefs = new ArrayList();
                        final IVirtualComponent[] refComponents = component.getReferencingComponents();
                        for (int j = 0; j < refComponents.length; j++) {
                            if (JavaEEProjectUtilities.isEARProject(refComponents[j].getProject())
                                    || JavaEEProjectUtilities.isDynamicWebProject(refComponents[j].getProject())) {
                                referencedFromEARorWAR = true;
                                earWarRefs.add(refComponents[j]);
                            }
                        }
                        if (!referencedFromEARorWAR) {
                            // warn if there are root mappings and the project is not referenced by an EAR or a WAR
                            final IMessage msg = new Message("classpathdependencyvalidator", //$NON-NLS-1$
                                    IMessage.NORMAL_SEVERITY, RootMappingNonEARWARRef, null, proj);
                            _reporter.addMessage(this, msg);
                        }
                    }
                }

                // generate warning messages for any potential entries; we warn for these since
                // the classes are being exposed but will not be bundled into the exported/published module and
                // therefore will not be available at runtime.
                i = potentialRawEntries.iterator();
                while (i.hasNext()) {
                    final IClasspathEntry entry = (IClasspathEntry) i.next();
                    final IMessage msg = new Message("classpathdependencyvalidator", //$NON-NLS-1$
                            IMessage.NORMAL_SEVERITY, NonTaggedExportedClasses,
                            new String[] { entry.getPath().toString() }, proj);
                    msg.setGroupName(entry.getPath().toString());
                    _reporter.addMessage(this, msg);
                }

                // validate all resolved entries (only perform this if there are raw referenced entries)
                if (!referencedRawEntries.isEmpty()) {
                    final Map referencedResolvedEntries = ClasspathDependencyUtil
                            .getComponentClasspathDependencies(javaProjectLite, isWebApp, false);
                    i = referencedResolvedEntries.keySet().iterator();
                    while (i.hasNext()) {
                        final IClasspathEntry entry = (IClasspathEntry) i.next();
                        final IClasspathAttribute attrib = (IClasspathAttribute) referencedResolvedEntries
                                .get(entry);
                        // compute the archive name
                        final String archivePath = ClasspathDependencyUtil.getArchiveName(entry);
                        if (archiveNames.contains(archivePath)) {
                            // Project cp entry
                            final IMessage msg = new Message("classpathdependencyvalidator", //$NON-NLS-1$
                                    IMessage.HIGH_SEVERITY, DuplicateArchiveName,
                                    new String[] { entry.getPath().toString() }, proj);
                            _reporter.addMessage(this, msg);
                        } else {
                            archiveNames.add(archivePath);
                        }
                        // validate the resolved entry if we didn't already validate as part of the raw entries
                        if (!referencedRawEntries.containsKey(entry)) {
                            IMessage[] msgs = validateVirtualComponentEntry(entry, attrib, isWebApp, proj, data);
                            reportMessages(msgs);
                        }
                    }
                }
            }
        } catch (CoreException e) {
            J2EEPlugin.logError(e);
        }

        return Status.OK_STATUS;
    }

    private void reportMessages(final IMessage[] msgs) {
        for (int i = 0; i < msgs.length; i++) {
            _reporter.addMessage(this, msgs[i]);
        }
    }

    /**
     * Checks if the specified Java classpath entry is a valid WTP virtual component reference.
     * Does not check the runtime path.
     * @param entry Raw or resolved classpath entry to validate. 
     * @param attrib The WTP classpath component dependency attribute. Null if it has not yet been set.
     * @param isWebApp True if the target project is associated with a web project.
     * @return IMessages representing validation results.
     */
    public static IMessage[] validateVirtualComponentEntry(final IClasspathEntry entry,
            final IClasspathAttribute attrib, final boolean isWebApp, final IProject project) {
        return validateVirtualComponentEntry(entry, attrib, isWebApp, project,
                new ClasspathDependencyValidatorData(project));
    }

    /**
     * Holds data required to validate classpath dependencies for a specific project. Can be computed once for the project and reused.
     *
     */
    public static class ClasspathDependencyValidatorData {
        private final IProject _project;
        // Class folders mapped via the component file (either directly or via src folders)
        private List<IContainer> javaOutputFolders;

        public ClasspathDependencyValidatorData(final IProject project) {
            this._project = project;
        }

        public IProject getProject() {
            return _project;
        }

        public List<IContainer> getJavaOutputFolders() {
            if (javaOutputFolders == null) {
                javaOutputFolders = JavaLiteUtilities
                        .getJavaOutputContainers(ComponentCore.createComponent(_project));
            }
            return javaOutputFolders;
        }

        /**
         * @deprecated use {@link #getJavaOutputFolders()}
         * @return
         */
        public IContainer[] getMappedClassFolders() {
            return getJavaOutputFolders().toArray(new IContainer[javaOutputFolders.size()]);
        }
    }

    /**
     * Checks if the specified Java classpath entry is a valid WTP virtual component reference.
     * Does not check the runtime path.
     * @param entry Raw or resolved classpath entry to validate. 
     * @param attrib The WTP classpath component dependency attribute. Null if it has not yet been set.
     * @param isWebApp True if the target project is associated with a web project.
     * @param project The parent project.
     * @param data Data required for validation. Can be computed once for the project and reused.
     * @return IMessages representing validation results.
     */
    public static IMessage[] validateVirtualComponentEntry(final IClasspathEntry entry,
            final IClasspathAttribute attrib, final boolean isWebApp, final IProject project,
            final ClasspathDependencyValidatorData data) {
        List results = new ArrayList();
        if (entry == null) {
            return (IMessage[]) results.toArray(new IMessage[results.size()]);
        }

        final int kind = entry.getEntryKind();
        final boolean isFile = !ClasspathDependencyUtil.isClassFolderEntry(entry);

        if (kind == IClasspathEntry.CPE_PROJECT) {
            // Project cp entry
            // Allow faceted projects only, and not plain java projects
            boolean isFacetedProject = false;
            IProject referencedProject = ResourcesPlugin.getWorkspace().getRoot()
                    .getProject(entry.getPath().toString());
            try {
                isFacetedProject = FacetedProjectFramework.isFacetedProject(referencedProject);
            } catch (CoreException ce) {
                //Ignore. Thrown when project metadata cannot be read. In that case we will treat the project as non faceted 
            }
            if (!isFacetedProject) {
                results.add(new Message("classpathdependencyvalidator", //$NON-NLS-1$
                        IMessage.HIGH_SEVERITY, ProjectClasspathEntry, new String[] { entry.getPath().toString() },
                        project));
                return (IMessage[]) results.toArray(new IMessage[results.size()]);
            }
        } else if (kind == IClasspathEntry.CPE_SOURCE) {

            // Source cp entry

            results.add(new Message("classpathdependencyvalidator", //$NON-NLS-1$
                    IMessage.HIGH_SEVERITY, SourceEntry, new String[] { entry.getPath().toString() }, project));
            return (IMessage[]) results.toArray(new IMessage[results.size()]);
        } else if (kind == IClasspathEntry.CPE_CONTAINER) {

            // get the set of classpath container IDs that should be filtered
            List filteredIDs = ClasspathDependencyExtensions.get().getFilteredClasspathContainerIDs();
            final IPath path = entry.getPath();
            for (int i = 0; i < filteredIDs.size(); i++) {
                final String id = (String) filteredIDs.get(i);
                if (path.segment(0).equals(id)) {
                    // filtered classpath container
                    results.add(new Message("classpathdependencyvalidator", //$NON-NLS-1$
                            IMessage.HIGH_SEVERITY, FilteredContainer, new String[] { entry.getPath().toString() },
                            project));
                    return (IMessage[]) results.toArray(new IMessage[results.size()]);
                }
            }

        } else if (kind == IClasspathEntry.CPE_LIBRARY) {
            if (!isFile) {
                final IContainer[] mappedClassFolders = data.getMappedClassFolders();
                final IResource resource = ClasspathDependencyUtil.getEntryResource(entry);
                if (resource != null) {
                    final IPath fullClassFolderPath = resource.getFullPath();
                    boolean alreadyMapped = false;
                    for (int j = 0; j < mappedClassFolders.length; j++) {
                        if (fullClassFolderPath.equals(mappedClassFolders[j].getFullPath())) {
                            // entry resolves to same file as existing class folder mapping, skip
                            alreadyMapped = true;
                            break;
                        }
                    }

                    // Class folder reference; ensure this is not already mapped via the component file.
                    if (alreadyMapped) {
                        results.add(new Message("classpathdependencyvalidator", //$NON-NLS-1$
                                IMessage.HIGH_SEVERITY, DuplicateClassFolderEntry,
                                new String[] { entry.getPath().toString() }, project));
                    }
                }
            }
        }

        //       final IPath runtimePath = ClasspathDependencyUtil.getRuntimePath(attrib, isWebApp, !isFile);
        //       if (!isWebApp) {
        //          // only a ../ or / mapping is currently legal in a non-web context
        //          if (!(runtimePath.equals(IClasspathDependencyConstants.RUNTIME_MAPPING_INTO_CONTAINER_PATH) 
        //                || runtimePath.equals(IClasspathDependencyConstants.RUNTIME_MAPPING_INTO_COMPONENT_PATH))) { 
        //             results.add(new Message("classpathdependencyvalidator", //$NON-NLS-1$
        //                   IMessage.HIGH_SEVERITY, InvalidNonWebRuntimePath, new String[]{entry.getPath().toString(), runtimePath.toString()}, project));
        //          }
        //       } else {
        //          String pathStr = runtimePath.toString();
        //          // can only be ../, /WEB-INF/lib or /WEB-INF/classes
        //          if (!runtimePath.equals(IClasspathDependencyConstants.RUNTIME_MAPPING_INTO_CONTAINER_PATH) 
        //             && !runtimePath.equals(IClasspathDependencyConstants.WEB_INF_LIB_PATH)
        //             && !runtimePath.equals(IClasspathDependencyConstants.WEB_INF_CLASSES_PATH)) { 
        //             results.add(new Message("classpathdependencyvalidator", //$NON-NLS-1$
        //                   IMessage.HIGH_SEVERITY, InvalidWebRuntimePath, new String[]{entry.getPath().toString(), pathStr}, project));
        //          }
        //       }

        return (IMessage[]) results.toArray(new IMessage[results.size()]);
    }

    public ISchedulingRule getSchedulingRule(IValidationContext helper) {
        return null;
    }

    public void cleanup(IReporter reporter) {
        _reporter = null;

    }

    public void validate(IValidationContext helper, IReporter reporter) throws ValidationException {
        // Forwarding to job method
        validateInJob(helper, reporter);
    }
}