javaee: rewrite JSP validation to run inside external build process and download required JARs (IDEA-206443)

This fixes several issues when validation wasn't performed correctly because required files weren't copied to the artifact; also the validator don't process PSI inside the IDE process to analyze dependencies (it processes class-files inside the build process), this will help to avoid performance issues.

The validator's JARs are removed from Git repository and don't bundled with the product anymore; they are attached as Maven libraries with 'provided' scope to the sources, and downloaded automatically when validation is invoked for the first time. This also helps to avoid issues with updating libraries on which they depend (e.g. Ant).

Tests are rewritten to use a simple mock implementation of Jasper validator; this way they run faster, don't depend on external libraries and don't require specific JDK version (previously it was needed to use Java 6 to run them).
This commit is contained in:
nik
2019-02-07 17:42:14 +03:00
parent 529bfd5ac2
commit 27b10c30d3
49 changed files with 108 additions and 311 deletions

View File

@@ -19,12 +19,14 @@ import com.intellij.openapi.compiler.CompileScope;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.api.CmdlineProtoUtil;
import org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
import org.jetbrains.jps.builders.BuildTargetType;
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
import org.jetbrains.jps.incremental.artifacts.ArtifactBuildTargetType;
import java.util.*;
@@ -140,4 +142,16 @@ public class CompileScopeUtil {
}
return scope instanceof OneProjectItemCompileScope || scope instanceof FileSetCompileScope;
}
public static TargetTypeBuildScope createScopeForArtifacts(Collection<Artifact> artifacts,
boolean forceBuild) {
TargetTypeBuildScope.Builder builder = TargetTypeBuildScope.newBuilder()
.setTypeId(ArtifactBuildTargetType.INSTANCE.getTypeId())
.setForceBuild(
forceBuild);
for (Artifact artifact : artifacts) {
builder.addTargetId(artifact.getName());
}
return builder.build();
}
}

View File

@@ -1,8 +1,9 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.compiler.options;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.Compiler;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.compiler.Validator;
import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration;
import com.intellij.openapi.compiler.options.ExcludesConfiguration;
import com.intellij.openapi.components.PersistentStateComponent;
@@ -21,9 +22,14 @@ import org.jetbrains.jps.model.serialization.java.compiler.JpsValidationSerializ
@State(name = JpsValidationSerializer.COMPONENT_NAME, storages = @Storage(JpsValidationSerializer.CONFIG_FILE_NAME))
public class ValidationConfiguration implements PersistentStateComponent<JpsValidationSerializer.ValidationConfigurationState> {
private final JpsValidationSerializer.ValidationConfigurationState myState = new JpsValidationSerializer.ValidationConfigurationState();
private final Project myProject;
public static boolean shouldValidate(Compiler validator, CompileContext context) {
ValidationConfiguration configuration = getInstance(context.getProject());
public ValidationConfiguration(Project project) {
myProject = project;
}
public static boolean shouldValidate(Compiler validator, Project project) {
ValidationConfiguration configuration = getInstance(project);
return (configuration.myState.VALIDATE_ON_BUILD) && configuration.isSelected(validator);
}
@@ -48,6 +54,12 @@ public class ValidationConfiguration implements PersistentStateComponent<JpsVali
setSelected(validator.getDescription(), selected);
}
public void deselectAllValidators() {
for (Validator validator : CompilerManager.getInstance(myProject).getCompilers(Validator.class)) {
myState.VALIDATORS.put(validator.getDescription(), false);
}
}
public void setSelected(String validatorDescription, boolean selected) {
myState.VALIDATORS.put(validatorDescription, selected);
}

View File

@@ -135,7 +135,7 @@ public class InspectionValidatorWrapper implements Validator {
@NotNull
public ProcessingItem[] getProcessingItems(final CompileContext context) {
final Project project = context.getProject();
if (project.isDefault() || !ValidationConfiguration.shouldValidate(this, context)) {
if (project.isDefault() || !ValidationConfiguration.shouldValidate(this, project)) {
return ProcessingItem.EMPTY_ARRAY;
}
final ExcludesConfiguration excludesConfiguration = ValidationConfiguration.getExcludedEntriesConfiguration(project);

View File

@@ -25,12 +25,8 @@ import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.impl.artifacts.ArtifactUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
import org.jetbrains.jps.incremental.artifacts.ArtifactBuildTargetType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
* @author nik
@@ -48,14 +44,8 @@ public class ArtifactBuildTargetScopeProvider extends BuildTargetScopeProvider {
CompileScopeUtil.addScopesForModules(modules, Collections.emptyList(), scopes, forceBuild);
}
if (!artifacts.isEmpty()) {
TargetTypeBuildScope.Builder builder = TargetTypeBuildScope.newBuilder()
.setTypeId(ArtifactBuildTargetType.INSTANCE.getTypeId())
.setForceBuild(
forceBuild || ArtifactCompileScope.isArtifactRebuildForced(baseScope));
for (Artifact artifact : artifacts) {
builder.addTargetId(artifact.getName());
}
scopes.add(builder.build());
boolean forceBuildForArtifacts = forceBuild || ArtifactCompileScope.isArtifactRebuildForced(baseScope);
scopes.add(CompileScopeUtil.createScopeForArtifacts(artifacts, forceBuildForArtifacts));
}
});

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
</web-app>

View File

@@ -1,12 +0,0 @@
<testData>
<delete>
</delete>
<deleted_by_make>
<file path="classes/x/S.class" />
</deleted_by_make>
<recompile>
<file path="root/a.jsp" />
</recompile>
</testData>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
</web-app>

View File

@@ -1,14 +0,0 @@
<%--
Created by IntelliJ IDEA.
User: cdr
Date: Sep 17, 2004
Time: 2:53:58 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="dep.jsp" %>
<%@include file="dep2.jsp" %>
<html>
<head><title>Simple jsp page</title></head>
<body>Place your content here</body>
</html>

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4" relativePaths="true" type="JAVA_MODULE">
<component name="ModuleRootManager" />
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/classes" />
<exclude-output />
<exploded url="file://$MODULE_DIR$/exploded" />
<exclude-exploded />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/lib/xxx.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntryProperties />
</component>
<component name="FacetManager">
<facet type="web" name="module">
<configuration>
<webroots>
<root url="file://$MODULE_DIR$/root" relative="/" />
</webroots>
<descriptors>
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/WEB-INF/web.xml" version="2.3" />
</descriptors>
</configuration>
</facet>
</component>
</module>

View File

@@ -1,7 +0,0 @@
<%@ page import="x.S"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% S.f(); %>
AAAAA

View File

@@ -1,7 +0,0 @@
package x;
//////////
public class S {
public static void f() {
}
}

View File

@@ -1,7 +0,0 @@
package x;
////////// XXXXXXX
public class S {
public static void f() {
}
}

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
</web-app>

View File

@@ -1,11 +0,0 @@
<testData>
<delete>
</delete>
<deleted_by_make>
</deleted_by_make>
<recompile>
<file path="root/a.jsp" />
</recompile>
</testData>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
</web-app>

View File

@@ -1,14 +0,0 @@
<%--
Created by IntelliJ IDEA.
User: cdr
Date: Sep 17, 2004
Time: 2:53:58 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="dep.jsp" %>
<%@include file="dep2.jsp" %>
<html>
<head><title>Simple jsp page</title></head>
<body>Place your content here</body>
</html>

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4" relativePaths="true" type="JAVA_MODULE">
<component name="ModuleRootManager" />
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/classes" />
<exclude-output />
<exploded url="file://$MODULE_DIR$/exploded" />
<exclude-exploded />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/lib/xxx.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntryProperties />
</component>
<component name="VcsManagerConfiguration">
<option name="ACTIVE_VCS_NAME" value="&lt;None&gt;" />
<option name="USE_PROJECT_VCS" value="true" />
</component>
<component name="FacetManager">
<facet type="web" name="module">
<configuration>
<webroots>
<root url="file://$MODULE_DIR$/root" relative="/" />
</webroots>
<packaging>
<containerElement type="module" name="module">
<attribute name="method" value="1" />
<attribute name="URI" value="/WEB-INF/classes" />
</containerElement>
<containerElement type="library" level="module">
<url>jar://$MODULE_DIR$/lib/xxx.jar!/</url>
<attribute name="method" value="1" />
<attribute name="URI" value="/WEB-INF/lib/xxx.jar" />
</containerElement>
</packaging>
<descriptors>
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/WEB-INF/web.xml" version="2.3" />
</descriptors>
<building>
<setting name="EXPLODED_URL" value="file://$MODULE_DIR$/exploded" />
<setting name="EXPLODED_ENABLED" value="true" />
<setting name="JAR_URL" value="file://$MODULE_DIR$/dep/war/module.war" />
<setting name="JAR_ENABLED" value="false" />
<setting name="SYNC_EXPLODED_DIR" value="true" />
<setting name="BUILD_ON_FRAME_DEACTIVATION" value="false" />
<setting name="RUN_JASPER_VALIDATION" value="true" />
</building>
</configuration>
</facet>
</component>
</module>

View File

@@ -1,14 +0,0 @@
<%--
Created by IntelliJ IDEA.
User: cdr
Date: Sep 17, 2004
Time: 2:53:58 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="dep.jsp" %>
<%@include file="dep2.jsp" %>
<html>
<head><title>Simple jsp page</title></head>
<body>Place your content here</body>
</html>

View File

@@ -1,11 +0,0 @@
<testData>
<delete>
</delete>
<deleted_by_make>
</deleted_by_make>
<recompile>
<file path="root/a.jsp" />
</recompile>
</testData>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
</web-app>

View File

@@ -1,14 +0,0 @@
<%--
Created by IntelliJ IDEA.
User: cdr
Date: Sep 17, 2004
Time: 2:53:58 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="dep.jsp" %>
<%@include file="dep2.jsp" %>
<html>
<head><title>Simple jsp page</title></head>
<body>Place your content here</body>
</html>

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4" relativePaths="true" type="JAVA_MODULE">
<component name="ModuleRootManager" />
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/classes" />
<exclude-output />
<exploded url="file://$MODULE_DIR$/exploded" />
<exclude-exploded />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="FacetManager">
<facet type="web" name="module">
<configuration>
<webroots>
<root url="file://$MODULE_DIR$/root" relative="/" />
</webroots>
<descriptors>
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/WEB-INF/web.xml" version="2.3" />
</descriptors>
</configuration>
</facet>
</component>
</module>

View File

@@ -1,14 +0,0 @@
<%--
Created by IntelliJ IDEA.
User: cdr
Date: Sep 17, 2004
Time: 2:53:58 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="dep.jsp" %>
<%@include file="dep2.jsp" %>
<html>
<head><title>Simple jsp page</title></head>
<body>Place your content here</body>
</html>

View File

@@ -17,6 +17,15 @@ public class JavaInMemoryCompiler {
private final JavaMemFileManager myFileManager = new JavaMemFileManager();
private final JavaCompiler myCompiler = ToolProvider.getSystemJavaCompiler();
public JavaInMemoryCompiler(File... classpath) {
try {
myFileManager.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(classpath));
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
public Map<String, byte[]> compile(String className, @Language("JAVA") String code) {
final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
final Iterable<? extends JavaFileObject> compilationUnits = Collections.singletonList(new JavaSourceFromString(className, code));
@@ -103,6 +112,10 @@ public class JavaInMemoryCompiler {
}
}
public void setLocation(Location location, Iterable<? extends File> path) throws IOException {
fileManager.setLocation(location, path);
}
public List<InMemoryClassFile> getClassFiles() {
return myClassFiles;
}

View File

@@ -0,0 +1,34 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.jps.builders.java.dependencyView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.org.objectweb.asm.ClassReader;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
public class ClassFileDependenciesAnalyzer {
private final DependencyContext myContext;
public ClassFileDependenciesAnalyzer(File dependenciesDataDir) throws IOException {
myContext = new DependencyContext(dependenciesDataDir);
}
@NotNull
public Set<String> collectDependencies(String className, ClassReader classReader) {
ClassFileRepr classFileRepr = new ClassfileAnalyzer(myContext).analyze(myContext.get(className), classReader);
if (classFileRepr == null) return Collections.emptySet();
final int classNameId = classFileRepr.name;
Set<String> classDependencies = new LinkedHashSet<>();
for (UsageRepr.Usage usage : classFileRepr.getUsages()) {
int owner = usage.getOwner();
if (owner != classNameId) {
classDependencies.add(myContext.getValue(owner));
}
}
return classDependencies;
}
}

View File

@@ -17,11 +17,17 @@ package org.jetbrains.jps.incremental.artifacts.impl;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.artifact.JpsArtifact;
import org.jetbrains.jps.model.artifact.elements.JpsComplexPackagingElement;
import org.jetbrains.jps.model.artifact.elements.JpsCompositePackagingElement;
import org.jetbrains.jps.model.artifact.elements.JpsModuleOutputPackagingElement;
import org.jetbrains.jps.model.artifact.elements.JpsPackagingElement;
import org.jetbrains.jps.model.module.JpsModule;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -60,4 +66,17 @@ public class JpsArtifactUtil {
public static boolean isArchiveName(String name) {
return name.length() >= 4 && name.charAt(name.length() - 4) == '.' && StringUtil.endsWithIgnoreCase(name, "ar");
}
public static Set<JpsModule> getModulesIncludedInArtifacts(final @NotNull Collection<? extends JpsArtifact> artifacts) {
final Set<JpsModule> modules = new THashSet<>();
for (JpsArtifact artifact : artifacts) {
processPackagingElements(artifact.getRootElement(), element -> {
if (element instanceof JpsModuleOutputPackagingElement) {
ContainerUtil.addIfNotNull(modules, ((JpsModuleOutputPackagingElement)element).getModuleReference().resolve());
}
return true;
});
}
return modules;
}
}

View File

@@ -24,10 +24,12 @@ import com.intellij.util.text.UniqueNameGenerator;
import org.jetbrains.jps.builders.BuildResult;
import org.jetbrains.jps.builders.CompileScopeTestBuilder;
import org.jetbrains.jps.builders.JpsBuildTestCase;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.JpsElementFactory;
import org.jetbrains.jps.model.artifact.DirectoryArtifactType;
import org.jetbrains.jps.model.artifact.JpsArtifact;
import org.jetbrains.jps.model.artifact.JpsArtifactService;
import org.jetbrains.jps.model.artifact.JpsArtifactType;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.java.JpsJavaLibraryType;
import org.jetbrains.jps.model.library.JpsLibrary;
@@ -94,8 +96,14 @@ public abstract class ArtifactBuilderTestCase extends JpsBuildTestCase {
}
protected JpsArtifact addArtifact(String name, LayoutElementTestUtil.LayoutElementCreator root) {
return addArtifact(name, root, DirectoryArtifactType.INSTANCE);
}
protected JpsArtifact addArtifact(String name, LayoutElementTestUtil.LayoutElementCreator root,
JpsArtifactType<JpsDummyElement> artifactType) {
assertFalse("JpsArtifact " + name + " already exists", getArtifactNames().contains(name));
JpsArtifact artifact = JpsArtifactService.getInstance().addArtifact(myProject, name, root.buildElement(), DirectoryArtifactType.INSTANCE,
JpsArtifact artifact = JpsArtifactService.getInstance().addArtifact(myProject, name, root.buildElement(),
artifactType,
JpsElementFactory.getInstance().createDummyElement());
artifact.setOutputPath(getAbsolutePath("out/artifacts/" + name));
return artifact;