blob: 1727e7d34cdec2850bc34fb6c96ec416609e373b [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.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.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jem.util.logger.proxy.Logger;
import org.eclipse.jst.j2ee.classpathdep.ClasspathDependencyUtil;
import org.eclipse.jst.j2ee.classpathdep.IClasspathDependencyConstants;
import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin;
import org.eclipse.jst.j2ee.internal.project.J2EEProjectUtilities;
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 ClassFolderEntry = "ClassFolderEntry"; //$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(JavaCore.NATURE_ID)) {
final IJavaProject javaProject = JavaCore.create(proj);
final boolean isWebApp = J2EEProjectUtilities.isDynamicWebProject(proj);
final Map referencedRawEntries = ClasspathDependencyUtil.getRawComponentClasspathDependencies(javaProject);
final List potentialRawEntries = ClasspathDependencyUtil.getPotentialComponentClasspathDependencies(javaProject);
final IVirtualComponent component = ComponentCore.createComponent(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 IClasspathAttribute attrib = (IClasspathAttribute) referencedRawEntries.get(entry);
final IPath runtimePath = ClasspathDependencyUtil.getRuntimePath(attrib, isWebApp);
if (runtimePath.equals(IClasspathDependencyConstants.RUNTIME_MAPPING_INTO_CONTAINER_PATH)) {
hasRootMapping = true;
}
IMessage[] msgs = validateVirtualComponentEntry(entry, attrib, isWebApp, proj);
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 (J2EEProjectUtilities.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 (J2EEProjectUtilities.isEARProject(refComponents[j].getProject())
|| J2EEProjectUtilities.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(javaProject, 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);
reportMessages(msgs);
}
}
}
}
} catch (CoreException e) {
Logger.getLogger(J2EEPlugin.PLUGIN_ID).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) {
List results = new ArrayList();
if (entry == null) {
return (IMessage[]) results.toArray(new IMessage[results.size()]);
}
final int kind = entry.getEntryKind();
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) {
// does the path refer to a file or a folder?
final IPath entryPath = entry.getPath();
IPath entryLocation = entryPath;
final IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(entryPath);
if (resource != null) {
entryLocation = resource.getLocation();
}
boolean isFile = true; // by default, assume a jar file
if (entryLocation.toFile().isDirectory()) {
isFile = false;
}
if (!isFile) {
// Class folder reference
results.add(new Message("classpathdependencyvalidator", // $NON-NLS-1$
IMessage.HIGH_SEVERITY, ClassFolderEntry, new String[]{entry.getPath().toString()}, project));
}
}
final IPath runtimePath = ClasspathDependencyUtil.getRuntimePath(attrib, isWebApp);
if (!isWebApp) {
// only a ../ mapping is currently legal in a non-web context
if (!runtimePath.equals(IClasspathDependencyConstants.RUNTIME_MAPPING_INTO_CONTAINER_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)
&& !pathStr.equals("/WEB-INF/lib")
&& !pathStr.equals("/WEB-INF/classes")) {
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);
}
}