diff --git a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenManifestGenerationBuildTaskProvider.java b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenManifestGenerationBuildTaskProvider.java index e5aa3003c36e..f65ee59c26c2 100644 --- a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenManifestGenerationBuildTaskProvider.java +++ b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/compiler/MavenManifestGenerationBuildTaskProvider.java @@ -16,26 +16,43 @@ package org.jetbrains.jps.maven.compiler; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Base64; +import com.intellij.util.Function; +import com.intellij.util.Processor; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.builders.BuildRootDescriptor; import org.jetbrains.jps.builders.artifacts.ArtifactBuildTaskProvider; import org.jetbrains.jps.builders.storage.BuildDataPaths; +import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.BuildTask; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ProjectBuildException; +import org.jetbrains.jps.incremental.artifacts.impl.JpsArtifactUtil; import org.jetbrains.jps.maven.model.JpsMavenExtensionService; import org.jetbrains.jps.maven.model.impl.MavenModuleResourceConfiguration; import org.jetbrains.jps.maven.model.impl.MavenProjectConfiguration; import org.jetbrains.jps.model.artifact.JpsArtifact; import org.jetbrains.jps.model.artifact.elements.JpsArtifactRootElement; +import org.jetbrains.jps.model.artifact.elements.JpsFileCopyPackagingElement; +import org.jetbrains.jps.model.artifact.elements.JpsPackagingElement; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.jar.Attributes; import java.util.jar.JarFile; +import java.util.jar.Manifest; /** * @author nik @@ -72,6 +89,7 @@ public class MavenManifestGenerationBuildTaskProvider extends ArtifactBuildTaskP try { File output = new File(myArtifact.getOutputPath(), JarFile.MANIFEST_NAME); FileUtil.writeToFile(output, Base64.decode(moduleResourceConfiguration.manifest)); + handleSkinnyWars(context, projectConfiguration, moduleResourceConfiguration); } // do not fail the whole 'Make' if there is an invalid manifest cached (e.g. non encoded string generated by previous IDEA version) catch (Exception e) { @@ -80,6 +98,82 @@ public class MavenManifestGenerationBuildTaskProvider extends ArtifactBuildTaskP } } + private void handleSkinnyWars(final CompileContext context, + final MavenProjectConfiguration projectConfiguration, + MavenModuleResourceConfiguration moduleResourceConfiguration) { + if (!"ear".equals(moduleResourceConfiguration.modelMap.get("packaging"))) return; + if (!Boolean.parseBoolean(moduleResourceConfiguration.modelMap.get("build.plugin.maven-ear-plugin.skinnyWars"))) return; + + final String earClasspath = moduleResourceConfiguration.classpath; + if (earClasspath == null) return; + + final Map earClasspathMap = ContainerUtil.map2Map( + StringUtil.split(earClasspath, " "), new Function>() { + @Override + public Pair fun(String s) { + final int idx = s.lastIndexOf("/"); + return Pair.create(s.substring(idx == -1 ? 0 : idx + 1), s); + } + }); + + JpsArtifactUtil.processPackagingElements(myArtifact.getRootElement(), new Processor() { + @Override + public boolean process(JpsPackagingElement element) { + if (!(element instanceof JpsFileCopyPackagingElement)) return true; + + final String filePath = ((JpsFileCopyPackagingElement)element).getFilePath(); + final File skinnyManifest = new File(filePath); + if (!"SKINNY_MANIFEST.MF".equals(skinnyManifest.getName())) return true; + + final String skinnyWarModuleName = skinnyManifest.getParentFile().getParentFile().getName(); + final MavenModuleResourceConfiguration warConfiguration = projectConfiguration.moduleConfigurations.get(skinnyWarModuleName); + if (warConfiguration == null || warConfiguration.classpath == null) return true; + + try { + final byte[] warManifestData = Base64.decode(warConfiguration.manifest); + Manifest warManifest = new Manifest(new ByteArrayInputStream(warManifestData)); + + List skinnyWarClasspath = ContainerUtil.newArrayList(); + for (String entry : StringUtil.split(warConfiguration.classpath, " ")) { + final int idx = entry.lastIndexOf("/"); + final String entryName = entry.substring(idx == -1 ? 0 : idx + 1); + final String earEntryPath = earClasspathMap.get(entryName); + skinnyWarClasspath.add(earEntryPath == null ? entry : earEntryPath); + } + + final Attributes warManifestMainAttributes = warManifest.getMainAttributes(); + warManifestMainAttributes.putValue("Class-Path", StringUtil.join(skinnyWarClasspath, " ")); + + FileUtil.createParentDirs(skinnyManifest); + FileOutputStream outputStream = new FileOutputStream(skinnyManifest); + try { + warManifest.write(outputStream); + + final ProjectDescriptor projectDescriptor = context.getProjectDescriptor(); + final Collection descriptors = + projectDescriptor.getBuildRootIndex().findAllParentDescriptors(skinnyManifest, context); + for (BuildRootDescriptor descriptor : descriptors) { + try { + projectDescriptor.fsState.markDirty(context, skinnyManifest, descriptor, projectDescriptor.timestamps.getStorage()); + } + catch (IOException e) { + LOG.debug(e); + } + } + } + finally { + StreamUtil.closeStream(outputStream); + } + } + catch (IOException e) { + LOG.debug(e); + } + + return true; + } + }); + } + @Nullable private static String getModuleName(@NotNull String artifactName) { return StringUtil.substringBefore(artifactName, ":"); diff --git a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/model/impl/MavenModuleResourceConfiguration.java b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/model/impl/MavenModuleResourceConfiguration.java index a6bf8b1e1e91..20cc5ed17b44 100644 --- a/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/model/impl/MavenModuleResourceConfiguration.java +++ b/plugins/maven/jps-plugin/src/org/jetbrains/jps/maven/model/impl/MavenModuleResourceConfiguration.java @@ -45,6 +45,10 @@ public class MavenModuleResourceConfiguration { @Tag("manifest") public String manifest; + @Nullable + @Tag("classpath") + public String classpath; + @NotNull @Tag("delimiters-pattern") public String delimitersPattern; @@ -110,6 +114,7 @@ public class MavenModuleResourceConfiguration { result = 31 * result + (parentId != null ? parentId.hashCode() : 0); result = 31 * result + directory.hashCode(); result = 31 * result + (manifest != null ? manifest.hashCode() : 0); + result = 31 * result + (classpath != null ? classpath.hashCode() : 0); result = 31 * result + delimitersPattern.hashCode(); result = 31 * result + modelMap.hashCode(); result = 31 * result + properties.hashCode(); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/ManifestImporter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/ManifestImporter.java index 65fb06cebfce..210f8013ee7e 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/ManifestImporter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/ManifestImporter.java @@ -50,7 +50,7 @@ public abstract class ManifestImporter { String classpathPrefix = getClasspathPrefix(manifestConfiguration); for (MavenArtifact mavenArtifact : mavenProject.getDependencies()) { final DependencyScope scope = MavenModuleImporter.selectScope(mavenArtifact.getScope()); - if (scope.isForProductionRuntime()) { + if (scope.isForProductionCompile() || scope.isForProductionRuntime()) { if (classpath.length() > 0) { classpath.append(" "); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenImporter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenImporter.java index f2e55d189ad1..7732bd15d3a7 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenImporter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenImporter.java @@ -149,6 +149,12 @@ public abstract class MavenImporter { Map mavenProjectToModuleName, List postTasks); + public void postProcess(Module module, + MavenProject mavenProject, + MavenProjectChanges changes, + MavenModifiableModelsProvider modifiableModelsProvider) { + } + public boolean processChangedModulesOnly() { return true; } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java index cb7396dfb85b..954706999ba3 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenModuleImporter.java @@ -159,6 +159,31 @@ public class MavenModuleImporter { }); } + public void postConfigFacets() { + MavenUtil.invokeAndWaitWriteAction(myModule.getProject(), new Runnable() { + public void run() { + if (myModule.isDisposed()) return; + + final ModuleType moduleType = ModuleType.get(myModule); + + for (final MavenImporter importer : getSuitableImporters()) { + final MavenProjectChanges changes; + if (myMavenProjectChanges == null) { + if (importer.processChangedModulesOnly()) continue; + changes = MavenProjectChanges.NONE; + } + else { + changes = myMavenProjectChanges; + } + + if (importer.getModuleType() == moduleType) { + importer.postProcess(myModule, myMavenProject, changes, myModifiableModelsProvider); + } + } + } + }); + } + private List getSuitableImporters() { return myMavenProject.getSuitableImporters(); } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenProjectImporter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenProjectImporter.java index 9ee85b83fcc5..a7b5a600516d 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenProjectImporter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/MavenProjectImporter.java @@ -431,7 +431,7 @@ public class MavenProjectImporter { javacOptions.ADDITIONAL_OPTIONS_STRING = options; } - private void importModules(final List postTasks) { + private void importModules(final List tasks) { Map projectsWithChanges = myProjectsToImportWithChanges; Set projectsWithNewlyCreatedModules = new THashSet(); @@ -471,7 +471,11 @@ public class MavenProjectImporter { } for (MavenModuleImporter importer : importers) { - importer.configFacets(postTasks); + importer.configFacets(tasks); + } + + for (MavenModuleImporter importer : importers) { + importer.postConfigFacets(); } setMavenizedModules(modulesToMavenize, true); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java index 274ee5a16193..5c4ad86a84c9 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java @@ -129,6 +129,8 @@ public class MavenResourceCompilerConfigurationGenerator { } } + addEarModelMapEntries(mavenProject, resourceConfig.modelMap); + Element pluginConfiguration = mavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-resources-plugin"); resourceConfig.outputDirectory = getResourcesPluginGoalOutputDirectory(mavenProject, pluginConfiguration, "resources"); @@ -190,6 +192,12 @@ public class MavenResourceCompilerConfigurationGenerator { }); } + private static void addEarModelMapEntries(@NotNull MavenProject mavenProject, @NotNull Map modelMap) { + Element pluginConfiguration = mavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-ear-plugin"); + final String skinnyWars = MavenJDOMUtil.findChildValueByPath(pluginConfiguration, "skinnyWars", "false"); + modelMap.put("build.plugin.maven-ear-plugin.skinnyWars", skinnyWars); + } + @Nullable private static String getResourcesPluginGoalOutputDirectory(@NotNull MavenProject mavenProject, @Nullable Element pluginConfiguration, @@ -234,6 +242,7 @@ public class MavenResourceCompilerConfigurationGenerator { finally { StreamUtil.closeStream(outputStream); } + resourceConfig.classpath = ManifestBuilder.getClasspath(mavenProject); } catch (ManifestBuilder.ManifestBuilderException e) { LOG.warn("Unable to generate artifact manifest", e); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/ManifestBuilder.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/ManifestBuilder.java index dbedc4a88675..bf6bf48abe1b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/ManifestBuilder.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/ManifestBuilder.java @@ -67,18 +67,7 @@ public class ManifestBuilder { @NotNull public java.util.jar.Manifest build() throws ManifestBuilderException { try { - Element mavenPackagingPluginConfiguration = null; - final String packaging = myMavenProject.getPackaging(); - if (StringUtil.isEmpty(packaging)) { - mavenPackagingPluginConfiguration = myMavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-jar-plugin"); - } - else { - final String pluginArtifactId = PACKAGING_PLUGINS.get(StringUtil.toLowerCase(packaging)); - if (pluginArtifactId != null) { - mavenPackagingPluginConfiguration = myMavenProject.getPluginConfiguration("org.apache.maven.plugins", pluginArtifactId); - } - } - + Element mavenPackagingPluginConfiguration = getMavenPackagingPluginConfiguration(myMavenProject); final Element mavenArchiveConfiguration = mavenPackagingPluginConfiguration != null ? mavenPackagingPluginConfiguration.getChild("archive") : null; @@ -110,6 +99,16 @@ public class ManifestBuilder { } } + @NotNull + public static String getClasspath(@NotNull MavenProject mavenProject) { + Element mavenPackagingPluginConfiguration = getMavenPackagingPluginConfiguration(mavenProject); + final Element mavenArchiveConfiguration = + mavenPackagingPluginConfiguration != null ? mavenPackagingPluginConfiguration.getChild("archive") : null; + final Element manifestConfiguration = mavenArchiveConfiguration != null ? mavenArchiveConfiguration.getChild("manifest") : null; + final ManifestImporter manifestImporter = ManifestImporter.getManifestImporter(mavenProject.getPackaging()); + return manifestImporter.getClasspath(mavenProject, manifestConfiguration); + } + @NotNull public static String getClasspathPrefix(@Nullable Element manifestConfiguration) { String classpathPrefix = MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "classpathPrefix", "").replaceAll("\\\\", "/"); @@ -119,6 +118,22 @@ public class ManifestBuilder { return classpathPrefix; } + @Nullable + private static Element getMavenPackagingPluginConfiguration(@NotNull MavenProject mavenProject) { + Element mavenPackagingPluginConfiguration = null; + final String packaging = mavenProject.getPackaging(); + if (StringUtil.isEmpty(packaging)) { + mavenPackagingPluginConfiguration = mavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-jar-plugin"); + } + else { + final String pluginArtifactId = PACKAGING_PLUGINS.get(StringUtil.toLowerCase(packaging)); + if (pluginArtifactId != null) { + mavenPackagingPluginConfiguration = mavenProject.getPluginConfiguration("org.apache.maven.plugins", pluginArtifactId); + } + } + return mavenPackagingPluginConfiguration; + } + private static Map getManifestEntries(Element manifestEntries) { boolean hasManifestEntries = manifestEntries != null && manifestEntries.getContentSize() > 0;