SoBeRich
SoBeRich

Reputation: 812

How to write MANIFEST.MF after Gradle WAR plugin processing, still using bnd features?

I need to pack a jar (or a war, format is irrelevant, please read further) with the structure of common war (wab) archive. Meaning that I need WEB-INF/classes/lib folder filled with dep-cy jars (not flattened/shaded) and all common stuff like resources, etc.

So, the main difference with the very common NON-osgi war would be a properly written manifest. The problem is that BND processes on the Jar task of Gradle, and then war plugin messes everything.

Right now I have reached the solution but I am not satisfied with it. And overall I feel there should be a better way. There are mostly two strategies:

  1. NOT using a war plugin and rely on BND rich capabilites.
  2. USE war pluging but rearrange somehow the build, so it does it's thing but lets the BND do its thing as well supposedly AFTER the war. (So, FIRST the WAR the BND is AFTER.)

A little input data. I believe the is a major SUPER_BUG in bnd since everything I said is supposed to work with just one directive -wablib, however, the major issue with it is that it works somehow in a diffwrent way rather than -includeresource for exemple. -wablib gets the input some sort of full_path_to_resource, which is not available while project evaluation and resolution by gradle and overall, it COULD be just brilliant IF it could take just a jar_name.jar as includeresource does. But it's not! So, even after trying to pull a string with full paths to all dependencies it is never done as gradle's configuration is not allowed to be manually resolved to get those paths.

On the other hand the second listed way works but it looks and feels like a crutch. Like it should not be that way. I mean manually constructing the Bundle-ClassPath and Include-Resources

How to painlessly create WARs WABs with war gradle plugin preferrably and not flattened/shaded?

Snippet (UPDATED)

I managed to avoid explicitly constructing manifestClasspath by just adding ;lib:=true so bnd does it's thing.

tasks.withType<Jar> {
    manifest {
        val manifestIncludeJars = configurations.
                implementation.
                incoming.
                dependencies.
                joinToString(",") {
                    "lib/${it.name}.jar=${it.name}-[0-9]*.jar;lib:=true"
                }
        attributes(mapOf(
                SNAPSHOT                to "\${tstamp}-SNAPSHOT",
                "Automatic-Module-Name" to "${project.group}.\${replace;\${bsn};[-_];.}",
                BUNDLE_VERSION          to project.version,
                INCLUDERESOURCE         to manifestIncludeJars
        ))
    }
}

UPDATE

Also I managed to get what I want by just using bnd without gradle 'war' plugin. But that's not what I want. This constructs the whole jar/war/wab whatever you call it with exactely the same structure as gradle 'war' plugin plus the proper manifest, but I want to separate the the proper manifest creation from assembling the archive so no delegation from gradle to bnd of the build tool functions

# [ WEB(APP) / WAB / WAR ]
-wab: src/main/webapp
-includeresource: WEB-INF/classes=src/main/resources
# This does the magic since gradle and bnd share properties during build
deps: ${project.sourceSets.main.runtimeClasspath.files}
# remove the square bracket form the last dependency so it to be incuded in archive
lastDep: {substring;${last;${deps}};0;-1}
-wablib: ${format;%s,;${filter;${deps},${lastDep};.*\.jar$}}

Upvotes: 1

Views: 601

Answers (1)

SoBeRich
SoBeRich

Reputation: 812

Eventually, BJ Hargrave said that there are no direct way to use WAR Gradle plugin and bnd Gradle plugin simultaneously, so the way to go is just one or another. Bnd Gradle plugin does not support same directory structure as canonical war project with Gradle WAR plugin, so there could be difficulties. Also, generated artifact despite has WAR-like structure, it is still a .jar. But generally, I could achieve the desired structure with just bnd-tools Gradle plugin.

P.S. The question and answer is only for non-workspace setup and for bnd-gradle-builder plugin

here is my war (e.g web) project's bnd.bnd (NON-workspace)

#     ___   ___     ____  ____________  ________ __________
#    / _ | / _ \__ / / / / / __/_  __/ /_  __/ // /  _/ __/
#   / __ |/ // / // / /_/ /\ \  / /     / / / _  // /_\ \
#  /_/ |_/____/\___/\____/___/ /_/     /_/ /_//_/___/___/
#
# ADJUST THIS

# [ BLUEPRINT ] //overrides Blueprint header if exists
Bundle-Blueprint:

# [ IMPEXP ]
-exportcontents: ${packages;ANNOTATED;org.osgi.annotation.versioning.Version}
Import-Package: *

# [ WEB(APP) / WAB / WAR ]
Web-ContextPath: /myapp
# //Pax Web support not 100% OSGi 4.2 compliant https://ops4j1.jira.com/browse/PAXWEB-206
Webapp-Context: /myapp
-wab: src/main/webapp
# // for instance regex for end and start (^\[)|(\]$)
-wablib: ${format;%s,;${filter;${substring;${project.sourceSets.main.runtimeClasspath.files};1;-1};.*\.jar$}}

#     __   ______ __________  __  _  ______    _______ _____   _  _______________
#    / /  /  _/ //_/ __/ /\ \/ / / |/ / __ \  / ___/ // / _ | / |/ / ___/ __/ __/
#   / /___/ // ,< / _// /__\  / /    / /_/ / / /__/ _  / __ |/    / (_ / _/_\ \
#  /____/___/_/|_/___/____//_/ /_/|_/\____/  \___/_//_/_/ |_/_/|_/\___/___/___/
#
# // probably no changes needed

Bundle-Description: ${project.description}
Bundle-DocURL: ${project.bundleDocURL}
Bundle-Version: ${project.version}
Automatic-Module-Name: ${project.group}.${replace;${bsn};[-_];.}

-snapshot: ${tstamp}-SNAPSHOT
-pedantic: true
-sources: false
-runee: JavaSE-${project.javaManifestVersion}
-pom: true
-fixupmessages: "Classes found in the wrong directory";is:=warning

non-war (non-web) project

#     ___   ___     ____  ____________  ________ __________
#    / _ | / _ \__ / / / / / __/_  __/ /_  __/ // /  _/ __/
#   / __ |/ // / // / /_/ /\ \  / /     / / / _  // /_\ \
#  /_/ |_/____/\___/\____/___/ /_/     /_/ /_//_/___/___/
#

# [ BLUEPRINT ] //overrides Blueprint header if exists
Bundle-Blueprint:

# [ IMPEXP ]
-exportcontents: ${packages;ANNOTATED;org.osgi.annotation.versioning.Version}
Import-Package: *

#     __   ______ __________  __  _  ______    _______ _____   _  _______________
#    / /  /  _/ //_/ __/ /\ \/ / / |/ / __ \  / ___/ // / _ | / |/ / ___/ __/ __/
#   / /___/ // ,< / _// /__\  / /    / /_/ / / /__/ _  / __ |/    / (_ / _/_\ \
#  /____/___/_/|_/___/____//_/ /_/|_/\____/  \___/_//_/_/ |_/_/|_/\___/___/___/
#

# // probably no changes needed
Bundle-Description: ${project.description}
Bundle-DocURL: ${project.bundleDocURL}
Bundle-Version: ${project.version}
Automatic-Module-Name: ${project.group}.${replace;${bsn};[-_];.}

-snapshot: ${tstamp}-SNAPSHOT
-pedantic: true
-sources: false
-runee: JavaSE-${project.javaManifestVersion}
-pom: true
-fixupmessages: "Classes found in the wrong directory";is:=warning

Also, since then bnd-tools started to support multi-jar projects, so fixupmessages could be avoided and maybe some other stuff should be adjusted accordingly.

Upvotes: 0

Related Questions