Skip to content

Commit c97bca2

Browse files
fix(licenses): support Java license designations (#636)
Signed-off-by: Kristina Pathak <kpathak@nvidia.com>
1 parent cc922d5 commit c97bca2

6 files changed

Lines changed: 213 additions & 30 deletions

File tree

dependencies.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,7 @@ Generated by `go run ./tools/collect-dependencies`. Refresh: `go run ./tools/col
647647
- `Java`: `net.javacrumbs.shedlock:shedlock-core:7.7.0` (net.javacrumbs.shedlock:shedlock-core; http://nexus.sonatype.org/oss-repository-hosting.html)
648648
- `Java`: `net.javacrumbs.shedlock:shedlock-provider-cassandra:7.7.0` (net.javacrumbs.shedlock:shedlock-provider-cassandra; http://nexus.sonatype.org/oss-repository-hosting.html)
649649
- `Java`: `net.javacrumbs.shedlock:shedlock-spring:7.7.0` (net.javacrumbs.shedlock:shedlock-spring; http://nexus.sonatype.org/oss-repository-hosting.html)
650+
- `Java`: `net.logstash.logback:logstash-logback-encoder:8.1` (Logstash Logback Encoder; https://github.com/logfellow/logstash-logback-encoder)
650651
- `Java`: `net.minidev:accessors-smart:2.6.0` (ASM based accessors helper used by json-smart; https://urielch.github.io/)
651652
- `Java`: `net.minidev:json-smart:2.6.0` (JSON Small and Fast Parser; https://urielch.github.io/)
652653
- `Java`: `org.apache.cassandra:java-driver-core:4.19.3` (Apache Cassandra Java Driver - core; https://github.com/datastax/java-driver)
@@ -671,13 +672,15 @@ Generated by `go run ./tools/collect-dependencies`. Refresh: `go run ./tools/col
671672
- `Java`: `org.apache.tomcat:tomcat-annotations-api:11.0.24` (tomcat-annotations-api; https://tomcat.apache.org/)
672673
- `Java`: `org.bitbucket.b_c:jose4j:0.9.6` (jose4j; https://bitbucket.org/b_c/jose4j/)
673674
- `Java`: `org.hibernate.validator:hibernate-validator:9.0.1.Final` (Hibernate Validator Engine; https://hibernate.org/validator)
675+
- `Java`: `org.javassist:javassist:3.28.0-GA` (Javassist; http://www.javassist.org/)
674676
- `Java`: `org.jboss.logging:jboss-logging:3.6.3.Final` (JBoss Logging 3; https://www.jboss.org)
675677
- `Java`: `org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21` (Kotlin Stdlib Jdk7; https://kotlinlang.org/)
676678
- `Java`: `org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21` (Kotlin Stdlib Jdk8; https://kotlinlang.org/)
677679
- `Java`: `org.jetbrains.kotlin:kotlin-stdlib:2.2.21` (Kotlin Stdlib; https://kotlinlang.org/)
678680
- `Java`: `org.jetbrains:annotations:17.0.0` (JetBrains Java Annotations; https://github.com/JetBrains/java-annotations)
679681
- `Java`: `org.jspecify:jspecify:1.0.0` (JSpecify annotations; http://jspecify.org/)
680682
- `Java`: `org.mapstruct:mapstruct:1.5.2.Final` (MapStruct Core; http://mapstruct.org/mapstruct/)
683+
- `Java`: `org.reflections:reflections:0.10.2` (Reflections; http://github.com/ronmamo/reflections)
681684
- `Java`: `org.springdoc:springdoc-openapi-starter-common:3.0.3` (springdoc-openapi-starter-common; https://springdoc.org/)
682685
- `Java`: `org.springdoc:springdoc-openapi-starter-webflux-api:3.0.3` (springdoc-openapi-starter-webflux-api; https://springdoc.org/)
683686
- `Java`: `org.springdoc:springdoc-openapi-starter-webmvc-api:3.0.3` (springdoc-openapi-starter-webmvc-api; https://springdoc.org/)
@@ -849,10 +852,6 @@ Generated by `go run ./tools/collect-dependencies`. Refresh: `go run ./tools/col
849852
- `Rust`: `stargate-tls`
850853
- `Rust`: `sync_wrapper 1`
851854

852-
## Apache-2.0 / LGPL 2.1 / MPL-1.1
853-
854-
- `Java`: `org.javassist:javassist:3.28.0-GA` (Javassist; http://www.javassist.org/)
855-
856855
## Apache-2.0 / LGPL-3.0-or-later
857856

858857
- `Java`: `com.github.java-json-tools:btf:1.3` (btf; https://github.com/java-json-tools/btf)
@@ -862,13 +861,8 @@ Generated by `go run ./tools/collect-dependencies`. Refresh: `go run ./tools/col
862861

863862
## Apache-2.0 / MIT
864863

865-
- `Java`: `net.logstash.logback:logstash-logback-encoder:8.1` (Logstash Logback Encoder; https://github.com/logfellow/logstash-logback-encoder)
866864
- `Rust`: `flume =0.12.0`
867865

868-
## Apache-2.0 / WTFPL
869-
870-
- `Java`: `org.reflections:reflections:0.10.2` (Reflections; http://github.com/ronmamo/reflections)
871-
872866
## Apache-2.0 OR ISC OR MIT
873867

874868
- `Rust`: `rustls` (`rustls 0.23.18`, `rustls =0.23.41`)

license-compliance.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,25 @@ deduplicated across components.
246246
`NOTICE`, runtime inventory, and OSRB delta outputs remain the component-level
247247
compliance evidence. None of these outputs replaces legal review.
248248

249+
### Java alternative license designations
250+
251+
Java component `notice_metadata.json` files retain the upstream `licenses`
252+
list. When an approved choice exists for a versioned dependency with
253+
alternative licenses, add `designated_license` with the chosen SPDX identifier.
254+
The generator accepts a listed license name or an alias in
255+
`tools/bazel/java/license_aliases.json`.
256+
257+
The generated component `NOTICE` records the designated license and the
258+
normalized upstream alternatives. The runtime inventory retains the alternatives
259+
in `declared_licenses` and reports the choice in `designated_license`. Its
260+
existing `licenses` field reports the designated license so dependency review
261+
uses the applicable choice.
262+
263+
Only record a designation supported by the dependency's license material and
264+
the required compliance review. Do not add private approval identifiers to
265+
public metadata. `--update-metadata` preserves an existing designation for the
266+
same coordinate and version, but does not carry it to a new version.
267+
249268
Use it when:
250269

251270
- imported trees or dependency manifests changed

src/control-plane-services/instance-cluster-management/NOTICE

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,15 @@ Lists of 293 third-party dependencies.
126126
(Apache License 2.0) swagger-annotations (io.swagger:swagger-annotations:1.6.16 - https://github.com/swagger-api/swagger-core)
127127
(EDL 1.0) Jakarta Activation API (jakarta.activation:jakarta.activation-api:2.1.4 - https://github.com/jakartaee/jaf-api)
128128
(EPL 2.0) (GPL2 w/ CPE) Jakarta Annotations API (jakarta.annotation:jakarta.annotation-api:3.0.0 - https://projects.eclipse.org/projects/ee4j.ca)
129-
(EPL 2.0) (GPL2 w/ CPE) Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.1.0 - https://projects.eclipse.org/projects/ee4j.servlet)
129+
(Designated: EPL-2.0; upstream: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0) Jakarta Servlet (jakarta.servlet:jakarta.servlet-api:6.1.0 - https://projects.eclipse.org/projects/ee4j.servlet)
130130
(Apache License 2.0) Jakarta Validation API (jakarta.validation:jakarta.validation-api:3.1.1 - https://beanvalidation.org)
131131
(Eclipse Distribution License - v 1.0) Jakarta XML Binding API (jakarta.xml.bind:jakarta.xml.bind-api:4.0.5 - https://github.com/jakartaee/jaxb-api)
132132
(CDDL + GPLv2 with classpath exception) javax.annotation API (javax.annotation:javax.annotation-api:1.3.2 - http://jcp.org/en/jsr/detail?id=250)
133133
(Apache 2) Joda-Time (joda-time:joda-time:2.8.1 - http://www.joda.org/joda-time/)
134134
(The Apache Software License, Version 2.0) net.javacrumbs.shedlock:shedlock-core (net.javacrumbs.shedlock:shedlock-core:7.7.0 - http://nexus.sonatype.org/oss-repository-hosting.html)
135135
(The Apache Software License, Version 2.0) net.javacrumbs.shedlock:shedlock-provider-cassandra (net.javacrumbs.shedlock:shedlock-provider-cassandra:7.7.0 - http://nexus.sonatype.org/oss-repository-hosting.html)
136136
(The Apache Software License, Version 2.0) net.javacrumbs.shedlock:shedlock-spring (net.javacrumbs.shedlock:shedlock-spring:7.7.0 - http://nexus.sonatype.org/oss-repository-hosting.html)
137-
(Apache License, Version 2.0) (MIT License) Logstash Logback Encoder (net.logstash.logback:logstash-logback-encoder:8.1 - https://github.com/logfellow/logstash-logback-encoder)
137+
(Designated: Apache-2.0; upstream: Apache-2.0 OR MIT) Logstash Logback Encoder (net.logstash.logback:logstash-logback-encoder:8.1 - https://github.com/logfellow/logstash-logback-encoder)
138138
(The Apache Software License, Version 2.0) ASM based accessors helper used by json-smart (net.minidev:accessors-smart:2.6.0 - https://urielch.github.io/)
139139
(The Apache Software License, Version 2.0) JSON Small and Fast Parser (net.minidev:json-smart:2.6.0 - https://urielch.github.io/)
140140
(Apache 2) Apache Cassandra Java Driver - core (org.apache.cassandra:java-driver-core:4.19.3 - https://github.com/datastax/java-driver)
@@ -164,7 +164,7 @@ Lists of 293 third-party dependencies.
164164
(Bouncy Castle Licence) Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk18on:1.80.2 - https://www.bouncycastle.org/download/bouncy-castle-java/)
165165
(Public Domain, per Creative Commons CC0) (BSD-2-Clause) HdrHistogram (org.hdrhistogram:HdrHistogram:2.2.2 - http://hdrhistogram.github.io/HdrHistogram/)
166166
(Apache License 2.0) Hibernate Validator Engine (org.hibernate.validator:hibernate-validator:9.0.1.Final - https://hibernate.org/validator)
167-
(MPL 1.1) (LGPL 2.1) (Apache License 2.0) Javassist (org.javassist:javassist:3.28.0-GA - http://www.javassist.org/)
167+
(Designated: Apache-2.0; upstream: Apache-2.0 OR LGPL 2.1 OR MPL 1.1) Javassist (org.javassist:javassist:3.28.0-GA - http://www.javassist.org/)
168168
(Apache License 2.0) JBoss Logging 3 (org.jboss.logging:jboss-logging:3.6.3.Final - https://www.jboss.org)
169169
(Apache-2.0) Kotlin Stdlib (org.jetbrains.kotlin:kotlin-stdlib:2.2.21 - https://kotlinlang.org/)
170170
(Apache-2.0) Kotlin Stdlib Jdk7 (org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21 - https://kotlinlang.org/)
@@ -179,7 +179,7 @@ Lists of 293 third-party dependencies.
179179
(BSD-3-Clause) asm-tree (org.ow2.asm:asm-tree:9.9 - http://asm.ow2.io/)
180180
(BSD-3-Clause) asm-util (org.ow2.asm:asm-util:9.9 - http://asm.ow2.io/)
181181
(MIT-0) reactive-streams (org.reactivestreams:reactive-streams:1.0.4 - http://www.reactive-streams.org/)
182-
(WTFPL) (The Apache Software License, Version 2.0) Reflections (org.reflections:reflections:0.10.2 - http://github.com/ronmamo/reflections)
182+
(Designated: Apache-2.0; upstream: Apache-2.0 OR WTFPL) Reflections (org.reflections:reflections:0.10.2 - http://github.com/ronmamo/reflections)
183183
(MIT) JUL to SLF4J bridge (org.slf4j:jul-to-slf4j:2.0.18 - http://www.slf4j.org)
184184
(MIT) SLF4J API Module (org.slf4j:slf4j-api:2.0.18 - http://www.slf4j.org)
185185
(The Apache License, Version 2.0) springdoc-openapi-starter-common (org.springdoc:springdoc-openapi-starter-common:3.0.3 - https://springdoc.org/)

src/control-plane-services/instance-cluster-management/notice_metadata.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@
274274
"url": "https://github.com/swagger-api/swagger-core"
275275
},
276276
"jakarta.servlet:jakarta.servlet-api:6.1.0": {
277+
"designated_license": "EPL-2.0",
277278
"licenses": [
278279
"EPL 2.0",
279280
"GPL2 w/ CPE"
@@ -317,6 +318,7 @@
317318
"url": "http://nexus.sonatype.org/oss-repository-hosting.html"
318319
},
319320
"net.logstash.logback:logstash-logback-encoder:8.1": {
321+
"designated_license": "Apache-2.0",
320322
"licenses": [
321323
"Apache License, Version 2.0",
322324
"MIT License"
@@ -430,6 +432,7 @@
430432
"url": "https://www.bouncycastle.org/download/bouncy-castle-java/"
431433
},
432434
"org.javassist:javassist:3.28.0-GA": {
435+
"designated_license": "Apache-2.0",
433436
"licenses": [
434437
"MPL 1.1",
435438
"LGPL 2.1",
@@ -460,6 +463,7 @@
460463
"url": "http://mapstruct.org/mapstruct/"
461464
},
462465
"org.reflections:reflections:0.10.2": {
466+
"designated_license": "Apache-2.0",
463467
"licenses": [
464468
"WTFPL",
465469
"The Apache Software License, Version 2.0"

tools/bazel/java/generate_notice.py

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,32 @@ def normalized_licenses(licenses, aliases):
131131
return sorted({aliases.get(license_name, license_name) for license_name in licenses})
132132

133133

134+
def designated_license(entry, aliases):
135+
designation = entry.get("designated_license")
136+
if designation is None:
137+
return ""
138+
if not isinstance(designation, str) or not designation.strip():
139+
raise ValueError("designated_license must be a non-empty string")
140+
141+
normalized_designation = aliases.get(designation, designation)
142+
declared = normalized_licenses(entry.get("licenses", []), aliases)
143+
if normalized_designation not in declared:
144+
raise ValueError(
145+
f"Designated license {designation!r} is not one of the upstream "
146+
f"licenses: {', '.join(declared) or 'none'}"
147+
)
148+
return normalized_designation
149+
150+
151+
def notice_license_text(entry, aliases):
152+
designation = designated_license(entry, aliases)
153+
if not designation:
154+
return " ".join(f"({license_name})" for license_name in entry.get("licenses", []))
155+
156+
declared = " OR ".join(normalized_licenses(entry.get("licenses", []), aliases))
157+
return f"(Designated: {designation}; upstream: {declared})"
158+
159+
134160
def load_root_manifests(paths):
135161
roots = []
136162
for path in paths:
@@ -235,7 +261,7 @@ def runtime_jar_coordinates(paths, maven_install, first_party_groups):
235261
return sorted(coordinates)
236262

237263

238-
def generated_notice(coordinates, maven_install, metadata):
264+
def generated_notice(coordinates, maven_install, metadata, aliases):
239265
lines = ["", f"Lists of {len(coordinates)} third-party dependencies."]
240266
missing = []
241267
incomplete = []
@@ -254,7 +280,7 @@ def generated_notice(coordinates, maven_install, metadata):
254280
if not licenses or "UNKNOWN" in licenses:
255281
incomplete.append(versioned)
256282
continue
257-
license_text = " ".join(f"({license_name})" for license_name in licenses)
283+
license_text = notice_license_text(entry, aliases)
258284
name = entry.get("name") or artifact_id
259285
url = entry.get("url") or ""
260286
lines.append(f" {license_text} {name} ({versioned} - {url})")
@@ -292,14 +318,18 @@ def generated_inventory(coordinates, maven_install, metadata, aliases):
292318
entry = metadata_artifacts(metadata).get(coordinate)
293319
if not entry:
294320
raise ValueError(f"Missing NOTICE metadata for {coordinate}")
295-
dependencies.append(
296-
{
297-
"coordinate": coordinate,
298-
"licenses": normalized_licenses(entry.get("licenses", []), aliases),
299-
"name": entry.get("name") or artifact_id,
300-
"url": entry.get("url") or "",
301-
}
302-
)
321+
declared = normalized_licenses(entry.get("licenses", []), aliases)
322+
designation = designated_license(entry, aliases)
323+
dependency = {
324+
"coordinate": coordinate,
325+
"licenses": [designation] if designation else declared,
326+
"name": entry.get("name") or artifact_id,
327+
"url": entry.get("url") or "",
328+
}
329+
if designation:
330+
dependency["declared_licenses"] = declared
331+
dependency["designated_license"] = designation
332+
dependencies.append(dependency)
303333
return {
304334
"generated_by": "tools/bazel/java/generate_notice.py",
305335
"dependencies": dependencies,
@@ -539,6 +569,7 @@ def update_metadata(
539569
maven_install,
540570
existing_metadata,
541571
shared_metadata=None,
572+
aliases=None,
542573
):
543574
resolver = PomMetadataResolver(maven_install)
544575
artifacts = dict(existing_metadata.get("artifacts", {}))
@@ -553,11 +584,16 @@ def update_metadata(
553584
if versioned in shared_artifacts:
554585
continue
555586
resolved = resolver.resolve(group_id, artifact_id, version)
556-
artifacts[versioned] = {
587+
updated_entry = {
557588
"licenses": resolved["licenses"],
558589
"name": resolved["name"],
559590
"url": resolved["url"],
560591
}
592+
existing = artifacts.get(versioned, {})
593+
if "designated_license" in existing:
594+
updated_entry["designated_license"] = existing["designated_license"]
595+
designated_license(updated_entry, aliases or {})
596+
artifacts[versioned] = updated_entry
561597
return {
562598
"generated_by": "tools/bazel/java/generate_notice.py --update-metadata",
563599
"artifacts": {key: artifacts[key] for key in sorted(artifacts)},
@@ -667,19 +703,24 @@ def main():
667703
else:
668704
raise ValueError("At least one --root-manifest or --runtime-jar is required")
669705

706+
aliases = load_license_aliases(
707+
pathlib.Path(args.license_aliases) if args.license_aliases else None
708+
)
709+
670710
if args.update_metadata:
671711
primary_metadata = update_metadata(
672712
coordinates,
673713
maven_install,
674714
primary_metadata,
675715
shared_metadata,
716+
aliases,
676717
)
677718
metadata_path.write_text(
678719
json.dumps(primary_metadata, indent=2, sort_keys=True) + "\n"
679720
)
680721
metadata = merge_metadata(shared_documents, primary_metadata)
681722

682-
notice = generated_notice(coordinates, maven_install, metadata)
723+
notice = generated_notice(coordinates, maven_install, metadata, aliases)
683724
output_path = pathlib.Path(args.output) if args.output else notice_path
684725
diff = compare_or_write(notice, output_path, args.write and not args.check)
685726
if diff:
@@ -695,11 +736,7 @@ def main():
695736
coordinates,
696737
maven_install,
697738
metadata,
698-
load_license_aliases(
699-
pathlib.Path(args.license_aliases)
700-
if args.license_aliases
701-
else None
702-
),
739+
aliases,
703740
)
704741
pathlib.Path(args.inventory_output).write_text(
705742
json.dumps(inventory, indent=2, sort_keys=True) + "\n"

0 commit comments

Comments
 (0)