blob: 1bff25dc2a3c521128b4b25cb02ca67c38ef8c7b [file] [log] [blame]
/*******************************************************************************
* 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.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.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 (proj.isAccessible()
&& proj.hasNature(ModuleCoreNature.MODULE_NATURE_ID)
&& proj.hasNature(JavaCoreLite.NATURE_ID)) {
final boolean isWebApp = JavaEEProjectUtilities.isDynamicWebProject(proj);
boolean webLibsOnly = false;
if(!ClasspathDependencyEnablement.isAllowClasspathComponentDependency()){
if(!isWebApp){
return OK_STATUS;
}
webLibsOnly = true;
}
final IJavaProjectLite javaProjectLite = JavaCoreLite.create(proj);
final Map referencedRawEntries = ClasspathDependencyUtil.getRawComponentClasspathDependencies(javaProjectLite, DependencyAttributeType.CLASSPATH_COMPONENT_DEPENDENCY, webLibsOnly);
final List potentialRawEntries = ClasspathDependencyUtil.getPotentialComponentClasspathDependencies(javaProjectLite, webLibsOnly);
final IVirtualComponent component = ComponentCore.createComponent(proj);
final ClasspathDependencyValidatorData data = new ClasspathDependencyValidatorData(proj);
// 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()) {
_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
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);
}
}