| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.idea.maven.utils; |
| |
| import com.intellij.openapi.application.ApplicationNamesInfo; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.io.StreamUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import org.codehaus.plexus.archiver.jar.Manifest; |
| import org.codehaus.plexus.archiver.jar.ManifestException; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.idea.maven.importing.ManifestImporter; |
| import org.jetbrains.idea.maven.project.MavenProject; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.jar.Attributes; |
| |
| import static org.codehaus.plexus.archiver.jar.Manifest.Attribute; |
| |
| /** |
| * @author Vladislav.Soroka |
| * @since 5/22/2014 |
| */ |
| public class ManifestBuilder { |
| |
| private static final Map<String, String> PACKAGING_PLUGINS = ContainerUtil.newHashMap( |
| Pair.create("jar", "maven-jar-plugin"), |
| Pair.create("ejb", "maven-ejb-plugin"), |
| Pair.create("ejb-client", "maven-ejb-plugin"), |
| Pair.create("war", "maven-war-plugin"), |
| Pair.create("ear", "maven-ear-plugin") |
| ); |
| |
| @NotNull private final MavenProject myMavenProject; |
| @Nullable private String myJdkVersion; |
| |
| public ManifestBuilder(@NotNull MavenProject mavenProject) { |
| myMavenProject = mavenProject; |
| } |
| |
| public ManifestBuilder withJdkVersion(String jdkVersion) { |
| myJdkVersion = jdkVersion; |
| return this; |
| } |
| |
| @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); |
| } |
| } |
| |
| final Element mavenArchiveConfiguration = |
| mavenPackagingPluginConfiguration != null ? mavenPackagingPluginConfiguration.getChild("archive") : null; |
| |
| if (mavenArchiveConfiguration == null) return getDefaultManifest(Collections.<String, String>emptyMap()); |
| |
| final Element manifestEntries = mavenArchiveConfiguration.getChild("manifestEntries"); |
| Map<String, String> entries = getManifestEntries(manifestEntries); |
| |
| final Element manifestConfiguration = mavenArchiveConfiguration.getChild("manifest"); |
| final Manifest configuredManifest = getConfiguredManifest(myMavenProject, manifestConfiguration, entries); |
| |
| if (!entries.isEmpty()) { |
| addManifestEntries(configuredManifest, entries); |
| } |
| |
| addCustomManifestSections(configuredManifest, mavenArchiveConfiguration); |
| |
| Manifest finalManifest = getDefaultManifest(entries); |
| // merge configured manifest |
| merge(finalManifest, configuredManifest); |
| |
| // merge user supplied manifest |
| final Manifest userSuppliedManifest = getUserSuppliedManifest(mavenArchiveConfiguration); |
| merge(finalManifest, userSuppliedManifest); |
| return finalManifest; |
| } |
| catch (ManifestException e) { |
| throw new ManifestBuilderException(e); |
| } |
| } |
| |
| @NotNull |
| public static String getClasspathPrefix(@Nullable Element manifestConfiguration) { |
| String classpathPrefix = MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "classpathPrefix", "").replaceAll("\\\\", "/"); |
| if (classpathPrefix.length() != 0 && !classpathPrefix.endsWith("/")) { |
| classpathPrefix += "/"; |
| } |
| return classpathPrefix; |
| } |
| |
| |
| private static Map<String, String> getManifestEntries(Element manifestEntries) { |
| boolean hasManifestEntries = manifestEntries != null && manifestEntries.getContentSize() > 0; |
| Map<String, String> entries = hasManifestEntries ? |
| new HashMap<String, String>(manifestEntries.getContentSize()) : |
| Collections.<String, String>emptyMap(); |
| if (hasManifestEntries) { |
| for (Element element : manifestEntries.getChildren()) { |
| entries.put(element.getName(), element.getTextTrim()); |
| } |
| } |
| return entries; |
| } |
| |
| private static void addCustomManifestSections(@NotNull Manifest manifest, @NotNull Element mavenArchiveConfiguration) |
| throws ManifestException { |
| |
| for (Element section : MavenJDOMUtil.findChildrenByPath(mavenArchiveConfiguration, "manifestSections", "manifestSection")) { |
| Manifest.Section theSection = new Manifest.Section(); |
| |
| final String sectionName = MavenJDOMUtil.findChildValueByPath(section, "name"); |
| theSection.setName(sectionName); |
| |
| final Element manifestEntries = section.getChild("manifestEntries"); |
| Map<String, String> entries = getManifestEntries(manifestEntries); |
| |
| if (!entries.isEmpty()) { |
| for (Map.Entry<String, String> entry : entries.entrySet()) { |
| Attribute attr = new Attribute(entry.getKey(), entry.getValue()); |
| theSection.addConfiguredAttribute(attr); |
| } |
| } |
| manifest.addConfiguredSection(theSection); |
| } |
| } |
| |
| @NotNull |
| private Manifest getDefaultManifest(@NotNull Map<String, String> entries) throws ManifestException { |
| Manifest finalManifest = new Manifest(); |
| addManifestAttribute(finalManifest, entries, "Created-By", ApplicationNamesInfo.getInstance().getFullProductName()); |
| addManifestAttribute(finalManifest, entries, "Built-By", System.getProperty("user.name")); |
| if (!StringUtil.isEmpty(myJdkVersion)) { |
| addManifestAttribute(finalManifest, entries, "Build-Jdk", myJdkVersion); |
| } |
| return finalManifest; |
| } |
| |
| private static void addManifestEntries(@NotNull Manifest manifest, @NotNull Map<String, String> entries) |
| throws ManifestException { |
| if (!entries.isEmpty()) { |
| for (Map.Entry<String, String> entry : entries.entrySet()) { |
| Attribute attr = manifest.getMainSection().getAttribute(entry.getKey()); |
| if ("Class-Path".equals(entry.getKey()) && attr != null) { |
| // Merge the user-supplied Class-Path value with the programmatically |
| // generated Class-Path. Note that the user-supplied value goes first |
| // so that resources there will override any in the standard Class-Path. |
| attr.setValue(entry.getValue() + " " + attr.getValue()); |
| } |
| else { |
| addManifestAttribute(manifest, entry.getKey(), entry.getValue()); |
| } |
| } |
| } |
| } |
| |
| @Nullable |
| private Manifest getUserSuppliedManifest(@Nullable Element mavenArchiveConfiguration) { |
| Manifest manifest = null; |
| String manifestPath = MavenJDOMUtil.findChildValueByPath(mavenArchiveConfiguration, "manifestFile"); |
| if (manifestPath != null) { |
| File manifestFile = new File(manifestPath); |
| if (!manifestFile.isAbsolute()) { |
| manifestFile = new File(myMavenProject.getDirectory(), manifestPath); |
| } |
| if (manifestFile.isFile()) { |
| FileInputStream fis = null; |
| try { |
| //noinspection IOResourceOpenedButNotSafelyClosed |
| fis = new FileInputStream(manifestFile); |
| manifest = new Manifest(fis); |
| } |
| catch (IOException ignore) { |
| } |
| finally { |
| StreamUtil.closeStream(fis); |
| } |
| } |
| } |
| |
| return manifest; |
| } |
| |
| @NotNull |
| private static Manifest getConfiguredManifest(@NotNull MavenProject mavenProject, |
| @Nullable Element manifestConfiguration, |
| @NotNull Map<String, String> entries) throws ManifestException { |
| final Manifest manifest = new Manifest(); |
| |
| boolean isAddDefaultSpecificationEntries = |
| Boolean.valueOf(MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "addDefaultSpecificationEntries", "false")); |
| if (isAddDefaultSpecificationEntries) { |
| addManifestAttribute(manifest, entries, "Specification-Title", mavenProject.getName()); |
| addManifestAttribute(manifest, entries, "Specification-Version", mavenProject.getMavenId().getVersion()); |
| } |
| |
| boolean isAddDefaultImplementationEntries = |
| Boolean.valueOf(MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "addDefaultImplementationEntries", "false")); |
| if (isAddDefaultImplementationEntries) { |
| addManifestAttribute(manifest, entries, "Implementation-Title", mavenProject.getName()); |
| addManifestAttribute(manifest, entries, "Implementation-Version", mavenProject.getMavenId().getVersion()); |
| addManifestAttribute(manifest, entries, "Implementation-Vendor-Id", mavenProject.getMavenId().getGroupId()); |
| } |
| |
| String packageName = MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "packageName"); |
| if (packageName != null) { |
| addManifestAttribute(manifest, entries, "Package", packageName); |
| } |
| |
| String mainClass = MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "mainClass"); |
| if (!StringUtil.isEmpty(mainClass)) { |
| addManifestAttribute(manifest, entries, "Main-Class", mainClass); |
| } |
| |
| boolean isAddClasspath = Boolean.valueOf(MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "addClasspath", "false")); |
| if (isAddClasspath) { |
| final ManifestImporter manifestImporter = ManifestImporter.getManifestImporter(mavenProject.getPackaging()); |
| String classpath = manifestImporter.getClasspath(mavenProject, manifestConfiguration); |
| if (!classpath.isEmpty()) { |
| addManifestAttribute(manifest, "Class-Path", classpath); |
| } |
| } |
| return manifest; |
| } |
| |
| private static void addManifestAttribute(@NotNull Manifest manifest, @NotNull Map<String, String> map, String key, String value) |
| throws ManifestException { |
| if (map.containsKey(key)) return; |
| addManifestAttribute(manifest, key, value); |
| } |
| |
| private static void addManifestAttribute(@NotNull Manifest manifest, String key, String value) throws ManifestException { |
| if (!StringUtil.isEmpty(value)) { |
| Attribute attr = new Attribute(key, value); |
| manifest.addConfiguredAttribute(attr); |
| } |
| } |
| |
| private static void merge(@NotNull java.util.jar.Manifest target, @Nullable java.util.jar.Manifest other) { |
| if (other != null) { |
| mergeAttributes(target.getMainAttributes(), other.getMainAttributes()); |
| |
| for (Map.Entry<String, Attributes> o : other.getEntries().entrySet()) { |
| Attributes ourSection = target.getAttributes(o.getKey()); |
| Attributes otherSection = o.getValue(); |
| if (ourSection == null) { |
| if (otherSection != null) { |
| target.getEntries().put(o.getKey(), (Attributes)otherSection.clone()); |
| } |
| } |
| else { |
| mergeAttributes(ourSection, otherSection); |
| } |
| } |
| } |
| } |
| |
| private static void mergeAttributes(@NotNull Attributes target, @NotNull Attributes section) { |
| for (Object o : section.keySet()) { |
| Attributes.Name key = (Attributes.Name)o; |
| final Object value = section.get(o); |
| target.put(key, value); |
| } |
| } |
| |
| |
| public static class ManifestBuilderException extends Exception { |
| public ManifestBuilderException(Throwable cause) { |
| super(cause); |
| } |
| } |
| } |