Skip to content

Commit d6fc9a5

Browse files
committed
Draft example Java file to generate a full SPDX V3 JSONLD file
1 parent 23bd470 commit d6fc9a5

3 files changed

Lines changed: 318 additions & 2 deletions

File tree

examples/org/spdx/examples/ExistingSpdxDocumentV2Compat.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@
3030
* for this example is assumed to be JSON (e.g. the output of the SimpleSpdxDocumentV2Compat example).
3131
* Different format can be used by using the associated store rather than the spdx-jackson store
3232
* (e.g. spdx-spreadsheet-store, spdx-tagvalue-store, or the spdx-rdf-store).
33-
*
33+
* <p>
3434
* This example depends on the Spdx-Java-Library and the spdx-java-jackson store libraries
35-
*
35+
* <p>
3636
* @author Gary O'Neall
3737
*/
3838
public class ExistingSpdxDocumentV2Compat {
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
package org.spdx.examples;
2+
3+
4+
import org.spdx.core.DefaultModelStore;
5+
import org.spdx.core.IModelCopyManager;
6+
import org.spdx.core.InvalidSPDXAnalysisException;
7+
import org.spdx.library.LicenseInfoFactory;
8+
import org.spdx.library.ModelCopyManager;
9+
import org.spdx.library.SpdxModelFactory;
10+
import org.spdx.library.model.v2.SpdxConstantsCompatV2;
11+
import org.spdx.library.model.v3_0_1.SpdxConstantsV3;
12+
import org.spdx.library.model.v3_0_1.SpdxModelClassFactoryV3;
13+
import org.spdx.library.model.v3_0_1.core.*;
14+
import org.spdx.library.model.v3_0_1.simplelicensing.AnyLicenseInfo;
15+
import org.spdx.library.model.v3_0_1.software.*;
16+
import org.spdx.storage.IModelStore;
17+
import org.spdx.storage.ISerializableModelStore;
18+
import org.spdx.storage.simple.InMemSpdxStore;
19+
import org.spdx.v3jsonldstore.JsonLDStore;
20+
21+
import java.io.File;
22+
import java.io.FileOutputStream;
23+
import java.io.OutputStream;
24+
import java.time.LocalDateTime;
25+
import java.time.format.DateTimeFormatter;
26+
import java.util.ArrayList;
27+
28+
/**
29+
* This class attempts to implement all the SPDX specification classes and most of the properties.
30+
* <p>
31+
* It will generate a resulting serialization that can be used as a full serialization example.
32+
* </p>
33+
* <p>
34+
* This example is current as of the version 3.0.1 of the SPDX Specification
35+
* </p>
36+
*/
37+
public class FullSpdxV3Example {
38+
39+
static final DateTimeFormatter SPDX_DATE_FORMATTER = DateTimeFormatter.ofPattern(SpdxConstantsCompatV2.SPDX_DATE_FORMAT);
40+
/**
41+
* @param args args[0] is the file path for the output serialized file
42+
*/
43+
public static void main(String[] args) throws Exception {
44+
if (args.length != 1) {
45+
usage();
46+
System.exit(1);
47+
}
48+
File outFile = new File(args[0]);
49+
if (outFile.exists()) {
50+
System.out.printf("%s already exists.\n", args[0]);
51+
System.exit(1);
52+
}
53+
if (!outFile.createNewFile()) {
54+
System.out.printf("Unable to create file %s\n", args[0]);
55+
System.exit(1);
56+
}
57+
if (!outFile.canWrite()) {
58+
System.out.printf("Can not write to file %s\n", args[0]);
59+
System.exit(1);
60+
}
61+
SpdxModelFactory.init();
62+
IModelCopyManager copyManager = new ModelCopyManager();
63+
try (ISerializableModelStore modelStore = new JsonLDStore(new InMemSpdxStore())) {
64+
String prefix = "https://spdx.github.io/spdx-spec/v3.0.1/examples/full-example-eaa46bdcfa20#";
65+
DefaultModelStore.initialize(modelStore, prefix, copyManager);
66+
CreationInfo creationInfo = SpdxModelClassFactoryV3.createCreationInfo(
67+
modelStore, prefix + "garyagent", "Gary O'Neall",
68+
copyManager);
69+
SpdxDocument doc = creationInfo.createSpdxDocument(prefix + "document")
70+
.setDataLicense(LicenseInfoFactory.getListedLicenseById("CC0"))
71+
.addNamespaceMap(creationInfo.createNamespaceMap(modelStore.getNextId(IModelStore.IdType.Anonymous))
72+
.setNamespace(prefix)
73+
.setPrefix("example")
74+
.build())
75+
.addProfileConformance(ProfileIdentifierType.CORE)
76+
.addProfileConformance(ProfileIdentifierType.SOFTWARE)
77+
.addProfileConformance(ProfileIdentifierType.BUILD)
78+
.addProfileConformance(ProfileIdentifierType.AI)
79+
.addProfileConformance(ProfileIdentifierType.DATASET)
80+
.addProfileConformance(ProfileIdentifierType.SECURITY)
81+
.addProfileConformance(ProfileIdentifierType.EXPANDED_LICENSING)
82+
.build();
83+
addCoreClasses(prefix, doc);
84+
Sbom sbom = addSoftwareClasses(prefix, doc);
85+
try (OutputStream outStream = new FileOutputStream(outFile)) {
86+
modelStore.serialize(outStream);
87+
}
88+
}
89+
}
90+
91+
private static void addCoreClasses(String prefix, SpdxDocument doc) throws InvalidSPDXAnalysisException {
92+
// Agent - Abstract, already in creation info
93+
// Annotation
94+
doc.getElements().add(doc.createAnnotation(prefix + "docannotation")
95+
.setStatement("This document is for example purposes only")
96+
.setAnnotationType(AnnotationType.OTHER)
97+
.setSubject(doc)
98+
.build());
99+
// Artifact - Abstract - used in software package and several others
100+
// Bom - will be used as an AI BOM and software BOM
101+
// Bundle
102+
doc.getElements().add(doc.createBundle(prefix + "bundle")
103+
.setComment("This is just an example of a concrete Bundle class - the elements are not used elsewhere in the SPDX document")
104+
.setContext("Custom Licenses")
105+
.addElement(doc.createCustomLicense(prefix + "LicenseRef-CustomLicense1")
106+
.setLicenseText("This is a custom license text number one.")
107+
.build())
108+
.addElement(doc.createCustomLicense(prefix + "LicenseRef-CustomLicense2")
109+
.setLicenseText("This is a custom license text number two.")
110+
.build())
111+
.build());
112+
// CreationInfo - Already created
113+
// DictionaryEntry - TODO: Change to make sure it has been created
114+
// Element - Abstract
115+
// ElementCollection - Abstract
116+
// ExternalIdentifier - TODO: Change to make sure it has been created
117+
// Organization
118+
doc.getCreationInfo().getCreatedBys().add(doc.createOrganization(prefix + "spdxorg")
119+
.setName("System Package Data Exchange (SPDX)")
120+
.build());
121+
// ExternalMap
122+
String orgLocation = "https://external/organization/spdxdata";
123+
String orgPrefix = orgLocation + "#";
124+
String orgUri = orgPrefix + "org";
125+
ExternalOrganization externalOrg = new ExternalOrganization(doc.getModelStore(),
126+
orgUri, doc.getCopyManager(),
127+
true, orgLocation);
128+
doc.getCreationInfo().getCreatedBys().add(externalOrg);
129+
doc.getSpdxImports().add(doc.createExternalMap(doc.getModelStore().getNextId(IModelStore.IdType.Anonymous))
130+
.setExternalSpdxId(orgUri)
131+
.setLocationHint(orgLocation)
132+
.build());
133+
// Hash - Used in file
134+
// IndividualElement - Used in software package originated by
135+
// IntegrityMethod - Used in file and package
136+
// LifecycleScopedRelationship - TODO: Change to make sure it has been created
137+
// NamespaceMap - Used in doc already
138+
// PackageVerificationCode - Going to ignore - deprecated
139+
// Person - Used in creation info
140+
// PositiveIntegerRange - TODO: Change to make sure it has been created
141+
// Relationship - Used in software
142+
// SoftwareAgent
143+
doc.getCreationInfo().getCreatedBys().add(doc.createSoftwareAgent(prefix + "softwareagent")
144+
.setName("SPDX Spec Github CI")
145+
.build());
146+
// SpdxDocument - already used
147+
// ExternalRef
148+
// Tool
149+
doc.getCreationInfo().getCreatedUsings().add(doc.createTool(prefix + "creationtool")
150+
.setName("tools-java")
151+
.setComment("Created by the FullSpdxV3Example.java utility in tools-java")
152+
.addExternalRef(doc.createExternalRef(doc.getModelStore().getNextId(IModelStore.IdType.Anonymous))
153+
.setExternalRefType(ExternalRefType.MAVEN_CENTRAL)
154+
.addLocator("org.spdx:tools-java")
155+
.build())
156+
.build());
157+
158+
}
159+
160+
private static Sbom addSoftwareClasses(String prefix, SpdxDocument doc) throws InvalidSPDXAnalysisException {
161+
// Sbom
162+
Sbom sbom = doc.createSbom(prefix + "aibom")
163+
.setName("AI SBOM")
164+
.addSbomType(SbomType.ANALYZED)
165+
.addProfileConformance(ProfileIdentifierType.CORE)
166+
.addProfileConformance(ProfileIdentifierType.SOFTWARE)
167+
.addProfileConformance(ProfileIdentifierType.BUILD)
168+
.addProfileConformance(ProfileIdentifierType.SECURITY)
169+
.addProfileConformance(ProfileIdentifierType.EXPANDED_LICENSING)
170+
.build();
171+
doc.getElements().add(sbom);
172+
doc.getRootElements().add(sbom);
173+
// Package
174+
SpdxPackage pkg = doc.createSpdxPackage(prefix + "tools-java")
175+
.setName("tools-java")
176+
.setPrimaryPurpose(SoftwarePurpose.APPLICATION)
177+
.addAdditionalPurpose(SoftwarePurpose.LIBRARY)
178+
.addAttributionText("Maintained by the SPDX Community")
179+
.setBuiltTime(LocalDateTime.of(2025, 10, 15, 9, 10)
180+
.format(SPDX_DATE_FORMATTER))
181+
// ContentIdentifier
182+
.addContentIdentifier(doc.createContentIdentifier(doc.getModelStore().getNextId(IModelStore.IdType.Anonymous))
183+
.setContentIdentifierType(ContentIdentifierType.GITOID)
184+
.setContentIdentifierValue("23bd470259f55641eb72b0c5d733edac014a4554")
185+
.build())
186+
.setCopyrightText("Copyright (c) Source Auditor Inc.")
187+
.setDescription("A command-line utility for creating, converting, comparing, and validating SPDX documents across multiple formats.")
188+
.setDownloadLocation("https://github.com/spdx/tools-java/releases/download/v2.0.2/tools-java-2.0.2.zip")
189+
.addExternalIdentifier(doc.createExternalIdentifier(doc.getModelStore().getNextId(IModelStore.IdType.Anonymous))
190+
.setExternalIdentifierType(ExternalIdentifierType.URL_SCHEME)
191+
.setIdentifier("https://github.com/spdx/tools-java")
192+
.setIssuingAuthority("GitHub")
193+
.build())
194+
.addExternalRef(doc.createExternalRef(doc.getModelStore().getNextId(IModelStore.IdType.Anonymous))
195+
.setExternalRefType(ExternalRefType.MAVEN_CENTRAL)
196+
.addLocator("org.spdx:tools-java:jar:2.0.2")
197+
.build())
198+
.setPackageUrl("pkg:maven/org.spdx/tools-java@2.0.2")
199+
.setPackageVersion("2.0.2")
200+
.setReleaseTime(LocalDateTime.of(2025, 10, 15, 11, 50)
201+
.format(SPDX_DATE_FORMATTER))
202+
.setSourceInfo("This package came from the original source - the official SPDX GitHub repo and build process")
203+
.addStandardName("SPDX Version 2.X and SPDX Version 3.0")
204+
.setHomePage("https://github.com/spdx/tools-java")
205+
.addOriginatedBy(new SpdxOrganization())
206+
.setSuppliedBy(new SpdxOrganization())
207+
.setSummary("A command-line utility for creating, converting, comparing, and validating SPDX documents across multiple formats.")
208+
.addSupportLevel(SupportType.LIMITED_SUPPORT)
209+
.setValidUntilTime(LocalDateTime.of(2027, 10, 15, 9, 10)
210+
.format(SPDX_DATE_FORMATTER))
211+
.addVerifiedUsing(doc.createHash(doc.getModelStore().getNextId(IModelStore.IdType.Anonymous))
212+
.setAlgorithm(HashAlgorithm.SHA256)
213+
.setHashValue("c37ce759c3867780d55791a1804101d288fa921e77ed791e6c053fd5d7513d0d")
214+
.build())
215+
.build();
216+
doc.getElements().add(pkg);
217+
sbom.getElements().add(pkg);
218+
sbom.getRootElements().add(pkg);
219+
// File
220+
SpdxFile sourceFile = doc.createSpdxFile(prefix + "example-source")
221+
.setPrimaryPurpose(SoftwarePurpose.SOURCE)
222+
.setContentType("text/plain")
223+
.setCopyrightText("Copyright (c) 2025 Source Auditor Inc.")
224+
.setFileKind(FileKindType.FILE)
225+
.setName("./examples/org/spdx/examples/FullSpdxV3Example.java")
226+
.build();
227+
sbom.getElements().add(sourceFile);
228+
doc.getElements().add(sourceFile);
229+
// Relationships - declared license, concluded license, generated from
230+
doc.getElements().add(doc.createRelationship(prefix + "example-source-to-pkg")
231+
.setRelationshipType(RelationshipType.GENERATES)
232+
.setFrom(sourceFile)
233+
.addTo(pkg)
234+
.build());
235+
AnyLicenseInfo declared = LicenseInfoFactory.parseSPDXLicenseString("Apache-2.0",
236+
doc.getModelStore(), prefix, doc.getCopyManager(), new ArrayList<>());
237+
AnyLicenseInfo concluded = LicenseInfoFactory.parseSPDXLicenseString("Apache-2.0",
238+
doc.getModelStore(), prefix, doc.getCopyManager(), new ArrayList<>());
239+
doc.getElements().add(doc.createRelationship(prefix + "source-declared")
240+
.setRelationshipType(RelationshipType.HAS_DECLARED_LICENSE)
241+
.setFrom(sourceFile)
242+
.addTo(declared)
243+
.build());
244+
doc.getElements().add(doc.createRelationship(prefix + "source-concluded")
245+
.setRelationshipType(RelationshipType.HAS_CONCLUDED_LICENSE)
246+
.setFrom(sourceFile)
247+
.addTo(concluded)
248+
.build());
249+
doc.getElements().add(doc.createRelationship(prefix + "pkg-declared")
250+
.setRelationshipType(RelationshipType.HAS_DECLARED_LICENSE)
251+
.setFrom(pkg)
252+
.addTo(declared)
253+
.build());
254+
doc.getElements().add(doc.createRelationship(prefix + "pkg-concluded")
255+
.setRelationshipType(RelationshipType.HAS_CONCLUDED_LICENSE)
256+
.setFrom(pkg)
257+
.addTo(concluded)
258+
.build());
259+
// Snippet
260+
Snippet snippet = doc.createSnippet(prefix + "snippet")
261+
.addAttributionText("Example code created by Gary O'Neall")
262+
.setDescription("Main method for the FullSpdxV3Example.java")
263+
.setCopyrightText("Copyright (c) 2025 Source Auditor Inc.")
264+
.setByteRange(doc.createPositiveIntegerRange(doc.getModelStore().getNextId(IModelStore.IdType.Anonymous))
265+
.setBeginIntegerRange(43)
266+
.setEndIntegerRange(89)
267+
.build())
268+
.setLineRange(doc.createPositiveIntegerRange(doc.getModelStore().getNextId(IModelStore.IdType.Anonymous))
269+
.setBeginIntegerRange(1548)
270+
.setEndIntegerRange(3955)
271+
.build())
272+
.setName("main(String[] args)")
273+
.setSnippetFromFile(sourceFile)
274+
.build();
275+
doc.getElements().add(snippet);
276+
sbom.getElements().add(snippet);
277+
doc.getElements().add(doc.createRelationship(prefix + "snippet-declared")
278+
.setRelationshipType(RelationshipType.HAS_DECLARED_LICENSE)
279+
.setFrom(snippet)
280+
.addTo(declared)
281+
.build());
282+
doc.getElements().add(doc.createRelationship(prefix + "snippet-concluded")
283+
.setRelationshipType(RelationshipType.HAS_CONCLUDED_LICENSE)
284+
.setFrom(snippet)
285+
.addTo(concluded)
286+
.build());
287+
// SoftwareArtifact - Abstract
288+
return sbom;
289+
}
290+
291+
292+
private static void addAIandDataClasses(String prefix, SpdxDocument doc) throws InvalidSPDXAnalysisException {
293+
Bom aiBom = doc.createBom(prefix + "aibom")
294+
.setName("AI SBOM")
295+
.addProfileConformance(ProfileIdentifierType.CORE)
296+
.addProfileConformance(ProfileIdentifierType.SOFTWARE)
297+
.addProfileConformance(ProfileIdentifierType.AI)
298+
.addProfileConformance(ProfileIdentifierType.DATASET)
299+
.build();
300+
doc.getElements().add(aiBom);
301+
doc.getRootElements().add(aiBom);
302+
}
303+
304+
private static void usage() {
305+
System.out.println("Generates an SPDX JSON-LD file containing all of the supported classes.");
306+
System.out.println("Usage: FullSpdxV3Example outputfile");
307+
}
308+
}

tools-java.iml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module version="4">
3+
<component name="AdditionalModuleElements">
4+
<content url="file://$MODULE_DIR$" dumb="true">
5+
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
6+
</content>
7+
</component>
8+
</module>

0 commit comments

Comments
 (0)