FOSSA License Compliance in Java

Overview

FOSSA is a license compliance tool that helps organizations manage open-source dependencies, ensure license compliance, and mitigate security risks. This guide covers comprehensive integration of FOSSA into Java development workflows for automated license compliance management.

Architecture Components

1. FOSSA API Client

@Component
public class FossaClient {
private final RestTemplate restTemplate;
private final String apiBaseUrl;
private final String apiKey;
private final ObjectMapper objectMapper;
public FossaClient(@Value("${fossa.api.key}") String apiKey,
@Value("${fossa.api.url:https://app.fossa.com}") String apiBaseUrl) {
this.apiKey = apiKey;
this.apiBaseUrl = apiBaseUrl;
this.objectMapper = new ObjectMapper();
this.restTemplate = createRestTemplate();
}
private RestTemplate createRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
// Add authentication interceptor
restTemplate.getInterceptors().add((request, body, execution) -> {
request.getHeaders().set("Authorization", "Bearer " + apiKey);
request.getHeaders().set("Content-Type", "application/json");
return execution.execute(request, body);
});
return restTemplate;
}
public Project getProject(String projectId) throws FossaException {
try {
String url = String.format("%s/api/projects/%s", apiBaseUrl, projectId);
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new FossaException("Failed to fetch project: " + response.getStatusCode());
}
return objectMapper.readValue(response.getBody(), Project.class);
} catch (Exception e) {
throw new FossaException("Failed to get project: " + projectId, e);
}
}
public List<Dependency> getProjectDependencies(String projectId) throws FossaException {
try {
String url = String.format("%s/api/projects/%s/dependencies", apiBaseUrl, projectId);
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new FossaException("Failed to fetch dependencies: " + response.getStatusCode());
}
return objectMapper.readValue(response.getBody(),
objectMapper.getTypeFactory().constructCollectionType(List.class, Dependency.class));
} catch (Exception e) {
throw new FossaException("Failed to get project dependencies", e);
}
}
public LicenseComplianceReport getLicenseCompliance(String projectId) throws FossaException {
try {
String url = String.format("%s/api/projects/%s/issues", apiBaseUrl, projectId);
Map<String, String> params = new HashMap<>();
params.put("type", "license");
ResponseEntity<String> response = restTemplate.getForEntity(
url + "?type={type}", String.class, params);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new FossaException("Failed to fetch license compliance: " + response.getStatusCode());
}
return parseLicenseCompliance(response.getBody());
} catch (Exception e) {
throw new FossaException("Failed to get license compliance", e);
}
}
public SecurityComplianceReport getSecurityCompliance(String projectId) throws FossaException {
try {
String url = String.format("%s/api/projects/%s/issues", apiBaseUrl, projectId);
Map<String, String> params = new HashMap<>();
params.put("type", "security");
ResponseEntity<String> response = restTemplate.getForEntity(
url + "?type={type}", String.class, params);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new FossaException("Failed to fetch security compliance: " + response.getStatusCode());
}
return parseSecurityCompliance(response.getBody());
} catch (Exception e) {
throw new FossaException("Failed to get security compliance", e);
}
}
public ScanResult triggerScan(String projectId, String revision) throws FossaException {
try {
String url = String.format("%s/api/projects/%s/scans", apiBaseUrl, projectId);
Map<String, String> scanRequest = new HashMap<>();
scanRequest.put("revision", revision);
ResponseEntity<String> response = restTemplate.postForEntity(
url, scanRequest, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new FossaException("Failed to trigger scan: " + response.getStatusCode());
}
return objectMapper.readValue(response.getBody(), ScanResult.class);
} catch (Exception e) {
throw new FossaException("Failed to trigger scan", e);
}
}
public List<Project> searchProjects(String query) throws FossaException {
try {
String url = String.format("%s/api/projects", apiBaseUrl);
Map<String, String> params = new HashMap<>();
params.put("query", query);
ResponseEntity<String> response = restTemplate.getForEntity(
url + "?query={query}", String.class, params);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new FossaException("Failed to search projects: " + response.getStatusCode());
}
return objectMapper.readValue(response.getBody(),
objectMapper.getTypeFactory().constructCollectionType(List.class, Project.class));
} catch (Exception e) {
throw new FossaException("Failed to search projects", e);
}
}
private LicenseComplianceReport parseLicenseCompliance(String jsonResponse) 
throws JsonProcessingException {
JsonNode rootNode = objectMapper.readTree(jsonResponse);
List<LicenseIssue> issues = new ArrayList<>();
if (rootNode.isArray()) {
for (JsonNode issueNode : rootNode) {
LicenseIssue issue = parseLicenseIssue(issueNode);
issues.add(issue);
}
}
return new LicenseComplianceReport(issues);
}
private SecurityComplianceReport parseSecurityCompliance(String jsonResponse) 
throws JsonProcessingException {
JsonNode rootNode = objectMapper.readTree(jsonResponse);
List<SecurityIssue> issues = new ArrayList<>();
if (rootNode.isArray()) {
for (JsonNode issueNode : rootNode) {
SecurityIssue issue = parseSecurityIssue(issueNode);
issues.add(issue);
}
}
return new SecurityComplianceReport(issues);
}
private LicenseIssue parseLicenseIssue(JsonNode issueNode) {
return new LicenseIssue(
issueNode.path("id").asText(),
issueNode.path("title").asText(),
issueNode.path("description").asText(),
LicenseIssueType.fromString(issueNode.path("type").asText()),
issueNode.path("severity").asText(),
issueNode.path("dependency").path("name").asText(),
issueNode.path("dependency").path("version").asText(),
issueNode.path("license").path("name").asText()
);
}
private SecurityIssue parseSecurityIssue(JsonNode issueNode) {
return new SecurityIssue(
issueNode.path("id").asText(),
issueNode.path("title").asText(),
issueNode.path("description").asText(),
issueNode.path("severity").asText(),
issueNode.path("cve").asText(),
issueNode.path("cvssScore").asDouble(),
issueNode.path("dependency").path("name").asText(),
issueNode.path("dependency").path("version").asText()
);
}
}

2. FOSSA Data Models

// Core FOSSA Models
public class Project {
private String id;
private String name;
private String title;
private String type;
private String locator;
private String revision;
private Instant createdAt;
private Instant updatedAt;
private ProjectStatus status;
private int dependencyCount;
private int issueCount;
// Getters and setters
}
public class Dependency {
private String id;
private String name;
private String version;
private String type;
private String locator;
private List<License> licenses;
private List<Vulnerability> vulnerabilities;
private DependencyUsage usage;
// Getters and setters
public static class License {
private String name;
private String spdxId;
private String category;
private boolean isFsfLibre;
private boolean isOsiApproved;
private String text;
// Getters and setters
}
public static class Vulnerability {
private String id;
private String title;
private String description;
private String severity;
private String cve;
private Double cvssScore;
private List<String> affectedVersions;
private List<String> patchedVersions;
// Getters and setters
}
public static class DependencyUsage {
private boolean isDirect;
private List<String> paths;
private String scope;
// Getters and setters
}
}
// Compliance Models
public class LicenseComplianceReport {
private final List<LicenseIssue> issues;
private final Instant generatedAt;
private final ComplianceSummary summary;
public LicenseComplianceReport(List<LicenseIssue> issues) {
this.issues = issues != null ? issues : new ArrayList<>();
this.generatedAt = Instant.now();
this.summary = calculateSummary();
}
private ComplianceSummary calculateSummary() {
long totalIssues = issues.size();
long blockingIssues = issues.stream()
.filter(issue -> issue.getSeverity().equals("BLOCKER"))
.count();
long warningIssues = issues.stream()
.filter(issue -> issue.getSeverity().equals("WARNING"))
.count();
boolean isCompliant = blockingIssues == 0;
return new ComplianceSummary(totalIssues, blockingIssues, warningIssues, isCompliant);
}
public List<LicenseIssue> getIssuesByType(LicenseIssueType type) {
return issues.stream()
.filter(issue -> issue.getType() == type)
.collect(Collectors.toList());
}
public List<LicenseIssue> getIssuesBySeverity(String severity) {
return issues.stream()
.filter(issue -> issue.getSeverity().equals(severity))
.collect(Collectors.toList());
}
// Getters
}
public class LicenseIssue {
private final String id;
private final String title;
private final String description;
private final LicenseIssueType type;
private final String severity;
private final String dependencyName;
private final String dependencyVersion;
private final String licenseName;
public LicenseIssue(String id, String title, String description, LicenseIssueType type,
String severity, String dependencyName, String dependencyVersion,
String licenseName) {
this.id = id;
this.title = title;
this.description = description;
this.type = type;
this.severity = severity;
this.dependencyName = dependencyName;
this.dependencyVersion = dependencyVersion;
this.licenseName = licenseName;
}
// Getters
}
public enum LicenseIssueType {
LICENSE_NOT_ALLOWED,
LICENSE_RESTRICTED,
COPYLEFT_VIOLATION,
LICENSE_CONFLICT,
UNKNOWN_LICENSE,
CUSTOM;
public static LicenseIssueType fromString(String type) {
if (type == null) return CUSTOM;
switch (type.toUpperCase()) {
case "LICENSE_NOT_ALLOWED": return LICENSE_NOT_ALLOWED;
case "LICENSE_RESTRICTED": return LICENSE_RESTRICTED;
case "COPYLEFT_VIOLATION": return COPYLEFT_VIOLATION;
case "LICENSE_CONFLICT": return LICENSE_CONFLICT;
case "UNKNOWN_LICENSE": return UNKNOWN_LICENSE;
default: return CUSTOM;
}
}
}
public class SecurityComplianceReport {
private final List<SecurityIssue> issues;
private final Instant generatedAt;
private final SecuritySummary summary;
public SecurityComplianceReport(List<SecurityIssue> issues) {
this.issues = issues != null ? issues : new ArrayList<>();
this.generatedAt = Instant.now();
this.summary = calculateSummary();
}
private SecuritySummary calculateSummary() {
long totalIssues = issues.size();
long criticalIssues = issues.stream()
.filter(issue -> "CRITICAL".equals(issue.getSeverity()))
.count();
long highIssues = issues.stream()
.filter(issue -> "HIGH".equals(issue.getSeverity()))
.count();
long mediumIssues = issues.stream()
.filter(issue -> "MEDIUM".equals(issue.getSeverity()))
.count();
double averageCvss = issues.stream()
.mapToDouble(SecurityIssue::getCvssScore)
.average()
.orElse(0.0);
return new SecuritySummary(totalIssues, criticalIssues, highIssues, 
mediumIssues, averageCvss);
}
public List<SecurityIssue> getIssuesBySeverity(String severity) {
return issues.stream()
.filter(issue -> issue.getSeverity().equals(severity))
.collect(Collectors.toList());
}
public List<SecurityIssue> getIssuesAboveCvss(double threshold) {
return issues.stream()
.filter(issue -> issue.getCvssScore() >= threshold)
.collect(Collectors.toList());
}
// Getters
}
public class SecurityIssue {
private final String id;
private final String title;
private final String description;
private final String severity;
private final String cve;
private final double cvssScore;
private final String dependencyName;
private final String dependencyVersion;
public SecurityIssue(String id, String title, String description, String severity,
String cve, double cvssScore, String dependencyName, 
String dependencyVersion) {
this.id = id;
this.title = title;
this.description = description;
this.severity = severity;
this.cve = cve;
this.cvssScore = cvssScore;
this.dependencyName = dependencyName;
this.dependencyVersion = dependencyVersion;
}
// Getters
}
public class ComplianceSummary {
private final long totalIssues;
private final long blockingIssues;
private final long warningIssues;
private final boolean isCompliant;
public ComplianceSummary(long totalIssues, long blockingIssues, 
long warningIssues, boolean isCompliant) {
this.totalIssues = totalIssues;
this.blockingIssues = blockingIssues;
this.warningIssues = warningIssues;
this.isCompliant = isCompliant;
}
public double getComplianceScore() {
return totalIssues > 0 ? 
(double) (totalIssues - blockingIssues) / totalIssues * 100 : 100.0;
}
// Getters
}
public class SecuritySummary {
private final long totalIssues;
private final long criticalIssues;
private final long highIssues;
private final long mediumIssues;
private final double averageCvss;
public SecuritySummary(long totalIssues, long criticalIssues, long highIssues,
long mediumIssues, double averageCvss) {
this.totalIssues = totalIssues;
this.criticalIssues = criticalIssues;
this.highIssues = highIssues;
this.mediumIssues = mediumIssues;
this.averageCvss = averageCvss;
}
public RiskLevel getOverallRisk() {
if (criticalIssues > 0) return RiskLevel.CRITICAL;
if (highIssues > 0) return RiskLevel.HIGH;
if (mediumIssues > 5) return RiskLevel.MEDIUM;
if (mediumIssues > 0 || totalIssues > 10) return RiskLevel.LOW;
return RiskLevel.NONE;
}
// Getters
}
public enum RiskLevel {
NONE,
LOW,
MEDIUM,
HIGH,
CRITICAL
}
public class ScanResult {
private String id;
private String projectId;
private String revision;
private ScanStatus status;
private Instant startedAt;
private Instant completedAt;
private int dependenciesFound;
private int issuesFound;
// Getters and setters
}
public enum ScanStatus {
PENDING,
RUNNING,
COMPLETED,
FAILED,
CANCELLED
}
public enum ProjectStatus {
ACTIVE,
INACTIVE,
ARCHIVED
}

License Policy Management

1. License Policy Engine

@Service
public class LicensePolicyService {
private final FossaClient fossaClient;
private final LicensePolicyRepository policyRepository;
private final LicenseCatalogService licenseCatalog;
public LicensePolicyService(FossaClient fossaClient,
LicensePolicyRepository policyRepository,
LicenseCatalogService licenseCatalog) {
this.fossaClient = fossaClient;
this.policyRepository = policyRepository;
this.licenseCatalog = licenseCatalog;
}
public PolicyComplianceResult evaluateCompliance(String projectId, String policyId) 
throws FossaException {
LicensePolicy policy = policyRepository.findById(policyId)
.orElseThrow(() -> new PolicyNotFoundException("Policy not found: " + policyId));
LicenseComplianceReport complianceReport = fossaClient.getLicenseCompliance(projectId);
List<Dependency> dependencies = fossaClient.getProjectDependencies(projectId);
return evaluateAgainstPolicy(complianceReport, dependencies, policy);
}
public PolicyValidationResult validateDependency(String dependencyName, String version, 
String policyId) {
LicensePolicy policy = policyRepository.findById(policyId)
.orElseThrow(() -> new PolicyNotFoundException("Policy not found: " + policyId));
DependencyInfo dependency = licenseCatalog.getDependencyInfo(dependencyName, version);
if (dependency == null) {
return PolicyValidationResult.unknown(dependencyName, version);
}
return validateDependencyAgainstPolicy(dependency, policy);
}
public List<PolicyViolation> checkForViolations(LicenseComplianceReport complianceReport,
LicensePolicy policy) {
List<PolicyViolation> violations = new ArrayList<>();
for (LicenseIssue issue : complianceReport.getIssues()) {
if (isPolicyViolation(issue, policy)) {
PolicyViolation violation = createPolicyViolation(issue, policy);
violations.add(violation);
}
}
return violations;
}
public LicensePolicy createPolicy(PolicyDefinition definition) {
LicensePolicy policy = new LicensePolicy(definition);
return policyRepository.save(policy);
}
public PolicyEnforcementResult enforcePolicy(String projectId, String policyId) 
throws FossaException {
PolicyComplianceResult compliance = evaluateCompliance(projectId, policyId);
if (compliance.isCompliant()) {
return PolicyEnforcementResult.compliant(projectId, policyId);
} else {
List<EnforcementAction> actions = generateEnforcementActions(compliance);
return PolicyEnforcementResult.nonCompliant(projectId, policyId, 
compliance.getViolations(), actions);
}
}
private PolicyComplianceResult evaluateAgainstPolicy(LicenseComplianceReport complianceReport,
List<Dependency> dependencies,
LicensePolicy policy) {
List<PolicyViolation> violations = checkForViolations(complianceReport, policy);
List<Dependency> nonCompliantDeps = findNonCompliantDependencies(dependencies, policy);
boolean isCompliant = violations.isEmpty() && nonCompliantDeps.isEmpty();
ComplianceScore score = calculateComplianceScore(dependencies.size(), 
nonCompliantDeps.size());
return new PolicyComplianceResult(policy.getId(), isCompliant, violations, 
nonCompliantDeps, score);
}
private PolicyValidationResult validateDependencyAgainstPolicy(DependencyInfo dependency,
LicensePolicy policy) {
for (License license : dependency.getLicenses()) {
LicensePolicy.Rule rule = policy.getRuleForLicense(license.getSpdxId());
if (rule != null && !rule.isAllowed()) {
return PolicyValidationResult.violation(dependency.getName(), 
dependency.getVersion(), license, rule);
}
}
return PolicyValidationResult.compliant(dependency.getName(), dependency.getVersion());
}
private boolean isPolicyViolation(LicenseIssue issue, LicensePolicy policy) {
LicensePolicy.Rule rule = policy.getRuleForLicense(issue.getLicenseName());
return rule != null && !rule.isAllowed();
}
private PolicyViolation createPolicyViolation(LicenseIssue issue, LicensePolicy policy) {
return new PolicyViolation(
issue.getId(),
issue.getDependencyName(),
issue.getDependencyVersion(),
issue.getLicenseName(),
policy.getRuleForLicense(issue.getLicenseName()),
issue.getSeverity()
);
}
private List<Dependency> findNonCompliantDependencies(List<Dependency> dependencies,
LicensePolicy policy) {
return dependencies.stream()
.filter(dep -> isDependencyNonCompliant(dep, policy))
.collect(Collectors.toList());
}
private boolean isDependencyNonCompliant(Dependency dependency, LicensePolicy policy) {
return dependency.getLicenses().stream()
.anyMatch(license -> {
LicensePolicy.Rule rule = policy.getRuleForLicense(license.getSpdxId());
return rule != null && !rule.isAllowed();
});
}
private ComplianceScore calculateComplianceScore(int totalDependencies, 
int nonCompliantDependencies) {
double score = totalDependencies > 0 ? 
(double) (totalDependencies - nonCompliantDependencies) / totalDependencies * 100 : 100.0;
ComplianceLevel level;
if (score >= 95) level = ComplianceLevel.EXCELLENT;
else if (score >= 80) level = ComplianceLevel.GOOD;
else if (score >= 60) level = ComplianceLevel.FAIR;
else level = ComplianceLevel.POOR;
return new ComplianceScore(score, level);
}
private List<EnforcementAction> generateEnforcementActions(PolicyComplianceResult compliance) {
List<EnforcementAction> actions = new ArrayList<>();
for (PolicyViolation violation : compliance.getViolations()) {
EnforcementAction action = createEnforcementAction(violation);
actions.add(action);
}
return actions;
}
private EnforcementAction createEnforcementAction(PolicyViolation violation) {
String description = String.format(
"Replace dependency %s:%s with license %s",
violation.getDependencyName(),
violation.getDependencyVersion(),
violation.getLicenseName()
);
return new EnforcementAction(
violation.getDependencyName(),
description,
EnforcementAction.Type.REPLACE_DEPENDENCY,
EnforcementAction.Priority.HIGH
);
}
}
// Policy Management Models
public class LicensePolicy {
private final String id;
private final String name;
private final String description;
private final PolicyDefinition definition;
private final Instant createdAt;
private final Instant updatedAt;
private final Map<String, Rule> rules;
public LicensePolicy(PolicyDefinition definition) {
this.id = UUID.randomUUID().toString();
this.name = definition.getName();
this.description = definition.getDescription();
this.definition = definition;
this.createdAt = Instant.now();
this.updatedAt = Instant.now();
this.rules = buildRules(definition);
}
private Map<String, Rule> buildRules(PolicyDefinition definition) {
Map<String, Rule> ruleMap = new HashMap<>();
for (PolicyDefinition.LicenseRule licenseRule : definition.getLicenseRules()) {
Rule rule = new Rule(licenseRule.getLicenseId(), licenseRule.isAllowed(), 
licenseRule.getConditions());
ruleMap.put(licenseRule.getLicenseId(), rule);
}
return ruleMap;
}
public Rule getRuleForLicense(String licenseId) {
return rules.get(licenseId);
}
public boolean isLicenseAllowed(String licenseId) {
Rule rule = rules.get(licenseId);
return rule != null && rule.isAllowed();
}
// Getters
public static class Rule {
private final String licenseId;
private final boolean allowed;
private final List<String> conditions;
public Rule(String licenseId, boolean allowed, List<String> conditions) {
this.licenseId = licenseId;
this.allowed = allowed;
this.conditions = conditions != null ? conditions : new ArrayList<>();
}
// Getters
}
}
public class PolicyDefinition {
private String name;
private String description;
private List<LicenseRule> licenseRules;
private List<PackageRule> packageRules;
private EnforcementSettings enforcement;
// Getters and setters
public static class LicenseRule {
private String licenseId;
private boolean allowed;
private List<String> conditions;
// Getters and setters
}
public static class PackageRule {
private String packageName;
private String versionPattern;
private boolean allowed;
private String reason;
// Getters and setters
}
public static class EnforcementSettings {
private boolean failOnViolation;
private boolean blockDownloads;
private List<String> notificationChannels;
// Getters and setters
}
}
public class PolicyComplianceResult {
private final String policyId;
private final boolean isCompliant;
private final List<PolicyViolation> violations;
private final List<Dependency> nonCompliantDependencies;
private final ComplianceScore complianceScore;
private final Instant evaluatedAt;
public PolicyComplianceResult(String policyId, boolean isCompliant,
List<PolicyViolation> violations,
List<Dependency> nonCompliantDependencies,
ComplianceScore complianceScore) {
this.policyId = policyId;
this.isCompliant = isCompliant;
this.violations = violations;
this.nonCompliantDependencies = nonCompliantDependencies;
this.complianceScore = complianceScore;
this.evaluatedAt = Instant.now();
}
// Getters
}
public class PolicyViolation {
private final String issueId;
private final String dependencyName;
private final String dependencyVersion;
private final String licenseName;
private final LicensePolicy.Rule violatedRule;
private final String severity;
public PolicyViolation(String issueId, String dependencyName, String dependencyVersion,
String licenseName, LicensePolicy.Rule violatedRule, String severity) {
this.issueId = issueId;
this.dependencyName = dependencyName;
this.dependencyVersion = dependencyVersion;
this.licenseName = licenseName;
this.violatedRule = violatedRule;
this.severity = severity;
}
// Getters
}
public class ComplianceScore {
private final double score;
private final ComplianceLevel level;
public ComplianceScore(double score, ComplianceLevel level) {
this.score = score;
this.level = level;
}
// Getters
}
public enum ComplianceLevel {
EXCELLENT(90, 100),
GOOD(80, 89),
FAIR(60, 79),
POOR(0, 59);
private final int minScore;
private final int maxScore;
ComplianceLevel(int minScore, int maxScore) {
this.minScore = minScore;
this.maxScore = maxScore;
}
public static ComplianceLevel fromScore(double score) {
for (ComplianceLevel level : values()) {
if (score >= level.minScore && score <= level.maxScore) {
return level;
}
}
return POOR;
}
}
public class PolicyValidationResult {
private final String dependencyName;
private final String version;
private final boolean compliant;
private final License violatingLicense;
private final LicensePolicy.Rule violatedRule;
private PolicyValidationResult(String dependencyName, String version, boolean compliant,
License violatingLicense, LicensePolicy.Rule violatedRule) {
this.dependencyName = dependencyName;
this.version = version;
this.compliant = compliant;
this.violatingLicense = violatingLicense;
this.violatedRule = violatedRule;
}
public static PolicyValidationResult compliant(String dependencyName, String version) {
return new PolicyValidationResult(dependencyName, version, true, null, null);
}
public static PolicyValidationResult violation(String dependencyName, String version,
License violatingLicense, 
LicensePolicy.Rule violatedRule) {
return new PolicyValidationResult(dependencyName, version, false, 
violatingLicense, violatedRule);
}
public static PolicyValidationResult unknown(String dependencyName, String version) {
return new PolicyValidationResult(dependencyName, version, false, null, null);
}
// Getters
}
public class PolicyEnforcementResult {
private final String projectId;
private final String policyId;
private final boolean compliant;
private final List<PolicyViolation> violations;
private final List<EnforcementAction> actions;
private final Instant enforcedAt;
public PolicyEnforcementResult(String projectId, String policyId, boolean compliant,
List<PolicyViolation> violations, 
List<EnforcementAction> actions) {
this.projectId = projectId;
this.policyId = policyId;
this.compliant = compliant;
this.violations = violations;
this.actions = actions;
this.enforcedAt = Instant.now();
}
public static PolicyEnforcementResult compliant(String projectId, String policyId) {
return new PolicyEnforcementResult(projectId, policyId, true, 
Collections.emptyList(), Collections.emptyList());
}
public static PolicyEnforcementResult nonCompliant(String projectId, String policyId,
List<PolicyViolation> violations,
List<EnforcementAction> actions) {
return new PolicyEnforcementResult(projectId, policyId, false, violations, actions);
}
// Getters
}
public class EnforcementAction {
private final String target;
private final String description;
private final Type type;
private final Priority priority;
public EnforcementAction(String target, String description, Type type, Priority priority) {
this.target = target;
this.description = description;
this.type = type;
this.priority = priority;
}
public enum Type {
REPLACE_DEPENDENCY,
REQUEST_APPROVAL,
BLOCK_DOWNLOAD,
NOTIFY_TEAM,
CREATE_ISSUE
}
public enum Priority {
LOW,
MEDIUM,
HIGH,
CRITICAL
}
// Getters
}

Build System Integration

1. Maven Plugin Integration

@Mojo(name = "fossa-analysis", defaultPhase = LifecyclePhase.VERIFY)
public class FossaMavenPlugin extends AbstractMojo {
@Parameter(property = "fossa.apiKey")
private String apiKey;
@Parameter(property = "fossa.projectId", required = true)
private String projectId;
@Parameter(property = "fossa.failOnViolation", defaultValue = "true")
private boolean failOnViolation;
@Parameter(property = "fossa.policyId")
private String policyId;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
getLog().info("Starting FOSSA license compliance analysis...");
FossaClient fossaClient = new FossaClient(apiKey, "https://app.fossa.com");
LicensePolicyService policyService = createPolicyService(fossaClient);
// Trigger FOSSA analysis
ScanResult scanResult = fossaClient.triggerScan(projectId, getProjectRevision());
getLog().info("FOSSA scan triggered: " + scanResult.getId());
// Wait for analysis completion
waitForAnalysisCompletion(fossaClient, scanResult.getId());
// Check compliance
if (policyId != null) {
PolicyComplianceResult compliance = policyService.evaluateCompliance(projectId, policyId);
reportComplianceResults(compliance);
if (failOnViolation && !compliance.isCompliant()) {
throw new MojoFailureException("License compliance check failed. " +
"Found " + compliance.getViolations().size() + " policy violations.");
}
} else {
LicenseComplianceReport compliance = fossaClient.getLicenseCompliance(projectId);
reportBasicCompliance(compliance);
}
getLog().info("FOSSA analysis completed successfully");
} catch (Exception e) {
throw new MojoExecutionException("FOSSA analysis failed", e);
}
}
private String getProjectRevision() {
// Extract project revision from Maven properties
return "maven-" + getProject().getVersion();
}
private void waitForAnalysisCompletion(FossaClient fossaClient, String scanId) 
throws InterruptedException, FossaException {
int maxAttempts = 30;
int attempt = 0;
while (attempt < maxAttempts) {
ScanResult result = fossaClient.getScanResult(scanId);
if (result.getStatus() == ScanStatus.COMPLETED) {
getLog().info("FOSSA analysis completed");
return;
} else if (result.getStatus() == ScanStatus.FAILED) {
throw new FossaException("FOSSA analysis failed");
}
getLog().info("Waiting for FOSSA analysis to complete... (" + 
(attempt + 1) + "/" + maxAttempts + ")");
Thread.sleep(10000); // Wait 10 seconds
attempt++;
}
throw new FossaException("FOSSA analysis timed out");
}
private void reportComplianceResults(PolicyComplianceResult compliance) {
getLog().info("=== FOSSA License Compliance Report ===");
getLog().info("Policy: " + compliance.getPolicyId());
getLog().info("Compliant: " + compliance.isCompliant());
getLog().info("Compliance Score: " + compliance.getComplianceScore().getScore() + "%");
getLog().info("Violations: " + compliance.getViolations().size());
for (PolicyViolation violation : compliance.getViolations()) {
getLog().warn("VIOLATION: " + violation.getDependencyName() + ":" + 
violation.getDependencyVersion() + " - " + violation.getLicenseName());
}
}
private void reportBasicCompliance(LicenseComplianceReport compliance) {
getLog().info("=== FOSSA License Compliance Report ===");
getLog().info("Total Issues: " + compliance.getSummary().getTotalIssues());
getLog().info("Blocking Issues: " + compliance.getSummary().getBlockingIssues());
getLog().info("Compliant: " + compliance.getSummary().isCompliant());
for (LicenseIssue issue : compliance.getIssues()) {
getLog().warn("ISSUE: " + issue.getDependencyName() + ":" + 
issue.getDependencyVersion() + " - " + issue.getTitle());
}
}
private LicensePolicyService createPolicyService(FossaClient fossaClient) {
// Implementation to create policy service with dependencies
return new LicensePolicyService(fossaClient, new InMemoryPolicyRepository(),
new DefaultLicenseCatalogService());
}
}

2. Gradle Plugin Integration

public class FossaGradlePlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
FossaExtension extension = project.getExtensions().create("fossa", FossaExtension.class);
Task fossaAnalysisTask = project.getTasks().create("fossaAnalysis", FossaAnalysisTask.class, task -> {
task.setGroup("verification");
task.setDescription("Runs FOSSA license compliance analysis");
});
// Hook into check task
project.getTasks().getByName("check").dependsOn(fossaAnalysisTask);
// Configure default values
extension.getApiKey().convention(project.provider(() -> 
System.getenv("FOSSA_API_KEY")));
extension.getFailOnViolation().convention(true);
}
}
public class FossaAnalysisTask extends DefaultTask {
private final Property<String> apiKey;
private final Property<String> projectId;
private final Property<Boolean> failOnViolation;
private final Property<String> policyId;
@Inject
public FossaAnalysisTask() {
this.apiKey = getProject().getObjects().property(String.class);
this.projectId = getProject().getObjects().property(String.class);
this.failOnViolation = getProject().getObjects().property(Boolean.class);
this.policyId = getProject().getObjects().property(String.class);
}
@TaskAction
public void runAnalysis() {
getLogger().lifecycle("Starting FOSSA license compliance analysis...");
try {
FossaClient fossaClient = new FossaClient(apiKey.get(), "https://app.fossa.com");
LicensePolicyService policyService = createPolicyService(fossaClient);
// Trigger analysis
String revision = "gradle-" + getProject().getVersion();
ScanResult scanResult = fossaClient.triggerScan(projectId.get(), revision);
getLogger().lifecycle("FOSSA scan triggered: {}", scanResult.getId());
// Wait for completion
waitForAnalysisCompletion(fossaClient, scanResult.getId());
// Check compliance
if (policyId.isPresent()) {
PolicyComplianceResult compliance = policyService.evaluateCompliance(
projectId.get(), policyId.get());
reportComplianceResults(compliance);
if (failOnViolation.get() && !compliance.isCompliant()) {
throw new GradleException("License compliance check failed. " +
"Found " + compliance.getViolations().size() + " policy violations.");
}
} else {
LicenseComplianceReport compliance = fossaClient.getLicenseCompliance(projectId.get());
reportBasicCompliance(compliance);
}
getLogger().lifecycle("FOSSA analysis completed successfully");
} catch (Exception e) {
throw new GradleException("FOSSA analysis failed", e);
}
}
private void waitForAnalysisCompletion(FossaClient fossaClient, String scanId) 
throws InterruptedException, FossaException {
int maxAttempts = 30;
int attempt = 0;
while (attempt < maxAttempts) {
ScanResult result = fossaClient.getScanResult(scanId);
if (result.getStatus() == ScanStatus.COMPLETED) {
getLogger().lifecycle("FOSSA analysis completed");
return;
} else if (result.getStatus() == ScanStatus.FAILED) {
throw new FossaException("FOSSA analysis failed");
}
getLogger().lifecycle("Waiting for FOSSA analysis to complete... ({}/{})", 
attempt + 1, maxAttempts);
Thread.sleep(10000);
attempt++;
}
throw new FossaException("FOSSA analysis timed out");
}
private void reportComplianceResults(PolicyComplianceResult compliance) {
getLogger().lifecycle("=== FOSSA License Compliance Report ===");
getLogger().lifecycle("Policy: {}", compliance.getPolicyId());
getLogger().lifecycle("Compliant: {}", compliance.isCompliant());
getLogger().lifecycle("Compliance Score: {}%", compliance.getComplianceScore().getScore());
getLogger().lifecycle("Violations: {}", compliance.getViolations().size());
for (PolicyViolation violation : compliance.getViolations()) {
getLogger().warn("VIOLATION: {}:{} - {}", 
violation.getDependencyName(),
violation.getDependencyVersion(),
violation.getLicenseName());
}
}
private void reportBasicCompliance(LicenseComplianceReport compliance) {
getLogger().lifecycle("=== FOSSA License Compliance Report ===");
getLogger().lifecycle("Total Issues: {}", compliance.getSummary().getTotalIssues());
getLogger().lifecycle("Blocking Issues: {}", compliance.getSummary().getBlockingIssues());
getLogger().lifecycle("Compliant: {}", compliance.getSummary().isCompliant());
for (LicenseIssue issue : compliance.getIssues()) {
getLogger().warn("ISSUE: {}:{} - {}", 
issue.getDependencyName(),
issue.getDependencyVersion(),
issue.getTitle());
}
}
// Getters for properties
@Input
public Property<String> getApiKey() { return apiKey; }
@Input
public Property<String> getProjectId() { return projectId; }
@Input
public Property<Boolean> getFailOnViolation() { return failOnViolation; }
@Input
@Optional
public Property<String> getPolicyId() { return policyId; }
}
public class FossaExtension {
private final Property<String> apiKey;
private final Property<String> projectId;
private final Property<Boolean> failOnViolation;
private final Property<String> policyId;
@Inject
public FossaExtension(ObjectFactory objects) {
this.apiKey = objects.property(String.class);
this.projectId = objects.property(String.class);
this.failOnViolation = objects.property(Boolean.class);
this.policyId = objects.property(String.class);
}
// Getters
public Property<String> getApiKey() { return apiKey; }
public Property<String> getProjectId() { return projectId; }
public Property<Boolean> getFailOnViolation() { return failOnViolation; }
public Property<String> getPolicyId() { return policyId; }
}

CI/CD Integration

1. Jenkins Pipeline Integration

@Component
public class JenkinsFossaIntegration {
private final FossaClient fossaClient;
private final LicensePolicyService policyService;
private final ReportGenerator reportGenerator;
public JenkinsFossaIntegration(FossaClient fossaClient,
LicensePolicyService policyService,
ReportGenerator reportGenerator) {
this.fossaClient = fossaClient;
this.policyService = policyService;
this.reportGenerator = reportGenerator;
}
public PipelineComplianceResult checkComplianceInPipeline(String projectId, String buildId,
String policyId) throws FossaException {
getLogger().info("Starting FOSSA compliance check for build: " + buildId);
// Trigger FOSSA analysis
ScanResult scanResult = fossaClient.triggerScan(projectId, "build-" + buildId);
waitForAnalysisCompletion(scanResult.getId());
// Evaluate against policy
PolicyComplianceResult compliance = policyService.evaluateCompliance(projectId, policyId);
// Generate reports
String htmlReport = reportGenerator.generateComplianceReport(compliance);
String jsonReport = reportGenerator.generateJsonReport(compliance);
// Determine pipeline outcome
boolean shouldFail = !compliance.isCompliant();
String pipelineStatus = shouldFail ? "UNSTABLE" : "SUCCESS";
return new PipelineComplianceResult(
buildId,
projectId,
compliance,
pipelineStatus,
shouldFail,
htmlReport,
jsonReport
);
}
public void enforceComplianceGate(String projectId, String policyId) throws FossaException {
PolicyEnforcementResult enforcement = policyService.enforcePolicy(projectId, policyId);
if (!enforcement.isCompliant()) {
getLogger().warn("Compliance gate failed for project: " + projectId);
getLogger().warn("Violations: " + enforcement.getViolations().size());
// Implement enforcement actions
executeEnforcementActions(enforcement.getActions());
throw new ComplianceGateException("License compliance gate failed");
}
getLogger().info("Compliance gate passed for project: " + projectId);
}
public ComplianceTrend analyzeComplianceTrend(String projectId, Duration period) 
throws FossaException {
List<HistoricalCompliance> historicalData = fetchHistoricalCompliance(projectId, period);
return calculateComplianceTrend(historicalData);
}
private void waitForAnalysisCompletion(String scanId) throws FossaException {
int maxAttempts = 30;
int attempt = 0;
while (attempt < maxAttempts) {
ScanResult result = fossaClient.getScanResult(scanId);
if (result.getStatus() == ScanStatus.COMPLETED) {
return;
} else if (result.getStatus() == ScanStatus.FAILED) {
throw new FossaException("FOSSA analysis failed");
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new FossaException("Analysis wait interrupted", e);
}
attempt++;
}
throw new FossaException("FOSSA analysis timed out");
}
private void executeEnforcementActions(List<EnforcementAction> actions) {
for (EnforcementAction action : actions) {
switch (action.getType()) {
case REPLACE_DEPENDENCY:
getLogger().info("Enforcement: " + action.getDescription());
break;
case NOTIFY_TEAM:
notifyTeam(action);
break;
case CREATE_ISSUE:
createComplianceIssue(action);
break;
case BLOCK_DOWNLOAD:
blockDependencyDownload(action);
break;
}
}
}
private void notifyTeam(EnforcementAction action) {
// Implementation for team notification
getLogger().warn("TEAM NOTIFICATION: " + action.getDescription());
}
private void createComplianceIssue(EnforcementAction action) {
// Implementation for issue creation
getLogger().warn("COMPLIANCE ISSUE: " + action.getDescription());
}
private void blockDependencyDownload(EnforcementAction action) {
// Implementation for dependency blocking
getLogger().warn("BLOCKING DOWNLOAD: " + action.getDescription());
}
private List<HistoricalCompliance> fetchHistoricalCompliance(String projectId, Duration period) 
throws FossaException {
// Implementation to fetch historical compliance data
return Collections.emptyList();
}
private ComplianceTrend calculateComplianceTrend(List<HistoricalCompliance> historicalData) {
// Implementation to calculate compliance trends
return new ComplianceTrend();
}
private Logger getLogger() {
return LoggerFactory.getLogger(JenkinsFossaIntegration.class);
}
}
// Pipeline Integration Models
public class PipelineComplianceResult {
private final String buildId;
private final String projectId;
private final PolicyComplianceResult compliance;
private final String pipelineStatus;
private final boolean shouldFail;
private final String htmlReport;
private final String jsonReport;
public PipelineComplianceResult(String buildId, String projectId, 
PolicyComplianceResult compliance, String pipelineStatus,
boolean shouldFail, String htmlReport, String jsonReport) {
this.buildId = buildId;
this.projectId = projectId;
this.compliance = compliance;
this.pipelineStatus = pipelineStatus;
this.shouldFail = shouldFail;
this.htmlReport = htmlReport;
this.jsonReport = jsonReport;
}
// Getters
}
public class HistoricalCompliance {
private final Instant timestamp;
private final double complianceScore;
private final int totalDependencies;
private final int violations;
public HistoricalCompliance(Instant timestamp, double complianceScore,
int totalDependencies, int violations) {
this.timestamp = timestamp;
this.complianceScore = complianceScore;
this.totalDependencies = totalDependencies;
this.violations = violations;
}
// Getters
}
public class ComplianceTrend {
private final TrendDirection direction;
private final double changeRate;
private final String summary;
public ComplianceTrend() {
this.direction = TrendDirection.STABLE;
this.changeRate = 0.0;
this.summary = "No trend data available";
}
public ComplianceTrend(TrendDirection direction, double changeRate, String summary) {
this.direction = direction;
this.changeRate = changeRate;
this.summary = summary;
}
// Getters
}
public enum TrendDirection {
IMPROVING,
DETERIORATING,
STABLE
}
public class ComplianceGateException extends RuntimeException {
public ComplianceGateException(String message) {
super(message);
}
public ComplianceGateException(String message, Throwable cause) {
super(message, cause);
}
}

Reporting and Analytics

1. Compliance Reporting Service

@Service
public class ComplianceReportingService {
private final FossaClient fossaClient;
private final LicensePolicyService policyService;
private final TemplateEngine templateEngine;
private final ObjectMapper objectMapper;
public ComplianceReportingService(FossaClient fossaClient,
LicensePolicyService policyService) {
this.fossaClient = fossaClient;
this.policyService = policyService;
this.templateEngine = createTemplateEngine();
this.objectMapper = new ObjectMapper();
}
public String generateComplianceReport(PolicyComplianceResult compliance) {
Map<String, Object> model = new HashMap<>();
model.put("compliance", compliance);
model.put("generatedAt", Instant.now());
model.put("summary", compliance.getComplianceScore());
return templateEngine.process("compliance-report", 
new Context(Locale.getDefault(), model));
}
public String generateExecutiveSummary(String projectId, String policyId) 
throws FossaException {
PolicyComplianceResult compliance = policyService.evaluateCompliance(projectId, policyId);
Project project = fossaClient.getProject(projectId);
List<Dependency> dependencies = fossaClient.getProjectDependencies(projectId);
Map<String, Object> model = new HashMap<>();
model.put("project", project);
model.put("compliance", compliance);
model.put("dependencies", dependencies);
model.put("summary", createExecutiveSummary(compliance, dependencies));
return templateEngine.process("executive-summary", 
new Context(Locale.getDefault(), model));
}
public byte[] generatePdfReport(PolicyComplianceResult compliance) {
try {
String htmlContent = generateComplianceReport(compliance);
// Convert HTML to PDF using a library like iText or Flying Saucer
return convertHtmlToPdf(htmlContent);
} catch (Exception e) {
throw new RuntimeException("Failed to generate PDF report", e);
}
}
public String generateJsonReport(PolicyComplianceResult compliance) {
try {
Map<String, Object> report = new HashMap<>();
report.put("compliance", compliance);
report.put("generatedAt", Instant.now());
report.put("metadata", createReportMetadata());
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(report);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to generate JSON report", e);
}
}
public ComplianceDashboard generateDashboard(List<String> projectIds) throws FossaException {
ComplianceDashboard dashboard = new ComplianceDashboard();
for (String projectId : projectIds) {
Project project = fossaClient.getProject(projectId);
LicenseComplianceReport licenseReport = fossaClient.getLicenseCompliance(projectId);
SecurityComplianceReport securityReport = fossaClient.getSecurityCompliance(projectId);
ProjectComplianceStatus status = new ProjectComplianceStatus(
project,
licenseReport,
securityReport
);
dashboard.addProjectStatus(status);
}
dashboard.calculateOverallMetrics();
return dashboard;
}
private ExecutiveSummary createExecutiveSummary(PolicyComplianceResult compliance,
List<Dependency> dependencies) {
return new ExecutiveSummary(
compliance.isCompliant(),
compliance.getComplianceScore().getScore(),
dependencies.size(),
compliance.getViolations().size(),
compliance.getNonCompliantDependencies().size(),
extractTopViolations(compliance.getViolations())
);
}
private List<PolicyViolation> extractTopViolations(List<PolicyViolation> violations) {
return violations.stream()
.sorted(Comparator.comparing(PolicyViolation::getSeverity).reversed())
.limit(10)
.collect(Collectors.toList());
}
private Map<String, Object> createReportMetadata() {
Map<String, Object> metadata = new HashMap<>();
metadata.put("tool", "FOSSA Java Integration");
metadata.put("version", "1.0.0");
metadata.put("format", "JSON");
return metadata;
}
private byte[] convertHtmlToPdf(String htmlContent) {
// Implementation for HTML to PDF conversion
// This would use a library like iText or Flying Saucer
return htmlContent.getBytes(); // Placeholder
}
private TemplateEngine createTemplateEngine() {
TemplateEngine templateEngine = new TemplateEngine();
// Configure template resolver for Thymeleaf or similar
return templateEngine;
}
}
// Reporting Models
public class ExecutiveSummary {
private final boolean compliant;
private final double complianceScore;
private final int totalDependencies;
private final int totalViolations;
private final int nonCompliantDependencies;
private final List<PolicyViolation> topViolations;
public ExecutiveSummary(boolean compliant, double complianceScore, int totalDependencies,
int totalViolations, int nonCompliantDependencies,
List<PolicyViolation> topViolations) {
this.compliant = compliant;
this.complianceScore = complianceScore;
this.totalDependencies = totalDependencies;
this.totalViolations = totalViolations;
this.nonCompliantDependencies = nonCompliantDependencies;
this.topViolations = topViolations;
}
public String getComplianceStatus() {
return compliant ? "COMPLIANT" : "NON-COMPLIANT";
}
public String getRiskLevel() {
if (complianceScore >= 90) return "LOW";
if (complianceScore >= 70) return "MEDIUM";
if (complianceScore >= 50) return "HIGH";
return "CRITICAL";
}
// Getters
}
public class ComplianceDashboard {
private final List<ProjectComplianceStatus> projectStatuses;
private final Instant generatedAt;
private DashboardMetrics overallMetrics;
public ComplianceDashboard() {
this.projectStatuses = new ArrayList<>();
this.generatedAt = Instant.now();
}
public void addProjectStatus(ProjectComplianceStatus status) {
projectStatuses.add(status);
}
public void calculateOverallMetrics() {
long totalProjects = projectStatuses.size();
long compliantProjects = projectStatuses.stream()
.filter(ProjectComplianceStatus::isCompliant)
.count();
long totalDependencies = projectStatuses.stream()
.mapToLong(ProjectComplianceStatus::getTotalDependencies)
.sum();
long totalViolations = projectStatuses.stream()
.mapToLong(ProjectComplianceStatus::getTotalViolations)
.sum();
double averageComplianceScore = projectStatuses.stream()
.mapToDouble(ProjectComplianceStatus::getComplianceScore)
.average()
.orElse(0.0);
this.overallMetrics = new DashboardMetrics(
totalProjects,
compliantProjects,
totalDependencies,
totalViolations,
averageComplianceScore
);
}
// Getters
}
public class ProjectComplianceStatus {
private final Project project;
private final LicenseComplianceReport licenseReport;
private final SecurityComplianceReport securityReport;
private final Instant analyzedAt;
public ProjectComplianceStatus(Project project, LicenseComplianceReport licenseReport,
SecurityComplianceReport securityReport) {
this.project = project;
this.licenseReport = licenseReport;
this.securityReport = securityReport;
this.analyzedAt = Instant.now();
}
public boolean isCompliant() {
return licenseReport.getSummary().isCompliant() && 
securityReport.getSummary().getTotalIssues() == 0;
}
public double getComplianceScore() {
return licenseReport.getSummary().getComplianceScore();
}
public int getTotalDependencies() {
return project.getDependencyCount();
}
public int getTotalViolations() {
return licenseReport.getSummary().getTotalIssues() + 
securityReport.getSummary().getTotalIssues();
}
// Getters
}
public class DashboardMetrics {
private final long totalProjects;
private final long compliantProjects;
private final long totalDependencies;
private final long totalViolations;
private final double averageComplianceScore;
public DashboardMetrics(long totalProjects, long compliantProjects, long totalDependencies,
long totalViolations, double averageComplianceScore) {
this.totalProjects = totalProjects;
this.compliantProjects = compliantProjects;
this.totalDependencies = totalDependencies;
this.totalViolations = totalViolations;
this.averageComplianceScore = averageComplianceScore;
}
public double getComplianceRate() {
return totalProjects > 0 ? (double) compliantProjects / totalProjects * 100 : 0.0;
}
public double getViolationRate() {
return totalDependencies > 0 ? (double) totalViolations / totalDependencies * 100 : 0.0;
}
// Getters
}

Spring Boot Integration

1. Auto-Configuration

@Configuration
@EnableConfigurationProperties(FossaProperties.class)
public class FossaAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public FossaClient fossaClient(FossaProperties properties) {
return new FossaClient(properties.getApiKey(), properties.getApiUrl());
}
@Bean
@ConditionalOnMissingBean
public LicensePolicyService licensePolicyService(FossaClient fossaClient) {
return new LicensePolicyService(fossaClient, new InMemoryPolicyRepository(),
new DefaultLicenseCatalogService());
}
@Bean
@ConditionalOnMissingBean
public ComplianceReportingService complianceReportingService(FossaClient fossaClient,
LicensePolicyService policyService) {
return new ComplianceReportingService(fossaClient, policyService);
}
@Bean
@ConditionalOnMissingBean
public JenkinsFossaIntegration jenkinsFossaIntegration(FossaClient fossaClient,
LicensePolicyService policyService,
ComplianceReportingService reportingService) {
return new JenkinsFossaIntegration(fossaClient, policyService, reportingService);
}
@Bean
public FossaHealthIndicator fossaHealthIndicator(FossaClient fossaClient) {
return new FossaHealthIndicator(fossaClient);
}
}
@Component
public class FossaHealthIndicator implements HealthIndicator {
private final FossaClient fossaClient;
public FossaHealthIndicator(FossaClient fossaClient) {
this.fossaClient = fossaClient;
}
@Override
public Health health() {
try {
// Test API connectivity by searching for projects
fossaClient.searchProjects("test");
return Health.up()
.withDetail("service", "FOSSA API")
.withDetail("status", "connected")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("service", "FOSSA API")
.withDetail("error", e.getMessage())
.build();
}
}
}
@ConfigurationProperties(prefix = "fossa")
public class FossaProperties {
private String apiKey;
private String apiUrl = "https://app.fossa.com";
private String defaultProjectId;
private String defaultPolicyId;
private boolean enforceInBuild = true;
private Report report = new Report();
// Getters and setters
public static class Report {
private String format = "html";
private String outputDir = "./fossa-reports";
private boolean generatePdf = false;
private boolean generateJson = true;
// Getters and setters
}
}

2. REST API Controllers

@RestController
@RequestMapping("/api/fossa")
@Validated
public class FossaComplianceController {
private final FossaClient fossaClient;
private final LicensePolicyService policyService;
private final ComplianceReportingService reportingService;
public FossaComplianceController(FossaClient fossaClient,
LicensePolicyService policyService,
ComplianceReportingService reportingService) {
this.fossaClient = fossaClient;
this.policyService = policyService;
this.reportingService = reportingService;
}
@GetMapping("/projects/{projectId}/compliance")
public ResponseEntity<PolicyComplianceResult> getCompliance(
@PathVariable String projectId,
@RequestParam(required = false) String policyId) throws FossaException {
if (policyId != null) {
PolicyComplianceResult compliance = policyService.evaluateCompliance(projectId, policyId);
return ResponseEntity.ok(compliance);
} else {
LicenseComplianceReport compliance = fossaClient.getLicenseCompliance(projectId);
// Convert to policy compliance result
PolicyComplianceResult result = convertToPolicyResult(compliance);
return ResponseEntity.ok(result);
}
}
@PostMapping("/projects/{projectId}/scan")
public ResponseEntity<ScanResult> triggerScan(
@PathVariable String projectId,
@RequestBody ScanRequest request) throws FossaException {
ScanResult result = fossaClient.triggerScan(projectId, request.getRevision());
return ResponseEntity.accepted().body(result);
}
@GetMapping("/projects/{projectId}/report")
public ResponseEntity<String> getComplianceReport(
@PathVariable String projectId,
@RequestParam String policyId,
@RequestParam(defaultValue = "html") String format) throws FossaException {
PolicyComplianceResult compliance = policyService.evaluateCompliance(projectId, policyId);
switch (format.toLowerCase()) {
case "html":
String htmlReport = reportingService.generateComplianceReport(compliance);
return ResponseEntity.ok()
.contentType(MediaType.TEXT_HTML)
.body(htmlReport);
case "json":
String jsonReport = reportingService.generateJsonReport(compliance);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(jsonReport);
case "pdf":
byte[] pdfReport = reportingService.generatePdfReport(compliance);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.body(pdfReport);
default:
return ResponseEntity.badRequest().build();
}
}
@PostMapping("/policies")
public ResponseEntity<LicensePolicy> createPolicy(
@Valid @RequestBody PolicyDefinition definition) {
LicensePolicy policy = policyService.createPolicy(definition);
return ResponseEntity.status(HttpStatus.CREATED).body(policy);
}
@PostMapping("/enforce/{projectId}")
public ResponseEntity<PolicyEnforcementResult> enforcePolicy(
@PathVariable String projectId,
@RequestParam String policyId) throws FossaException {
PolicyEnforcementResult result = policyService.enforcePolicy(projectId, policyId);
return ResponseEntity.ok(result);
}
@GetMapping("/dashboard")
public ResponseEntity<ComplianceDashboard> getDashboard(
@RequestParam List<String> projectIds) throws FossaException {
ComplianceDashboard dashboard = reportingService.generateDashboard(projectIds);
return ResponseEntity.ok(dashboard);
}
private PolicyComplianceResult convertToPolicyResult(LicenseComplianceReport compliance) {
// Implementation to convert basic compliance to policy result
return new PolicyComplianceResult("default", compliance.getSummary().isCompliant(),
Collections.emptyList(), Collections.emptyList(),
new ComplianceScore(compliance.getSummary().getComplianceScore(),
ComplianceLevel.fromScore(compliance.getSummary().getComplianceScore())));
}
}
// Request DTOs
public class ScanRequest {
@NotBlank
private String revision;
private String branch;
// Getters and setters
}

Configuration

1. Application Configuration

fossa:
api-key: ${FOSSA_API_KEY}
api-url: https://app.fossa.com
default-project-id: ${FOSSA_PROJECT_ID}
default-policy-id: corporate-policy
enforce-in-build: true
report:
format: html
output-dir: ./fossa-reports
generate-pdf: false
generate-json: true
management:
endpoints:
web:
exposure:
include: "health,fossa"
endpoint:
fossa:
enabled: true
logging:
level:
com.example.fossa: DEBUG

Exception Handling

public class FossaException extends Exception {
public FossaException(String message) {
super(message);
}
public FossaException(String message, Throwable cause) {
super(message, cause);
}
}
public class PolicyNotFoundException extends RuntimeException {
public PolicyNotFoundException(String message) {
super(message);
}
}
@ControllerAdvice
public class FossaExceptionHandler {
@ExceptionHandler(FossaException.class)
public ResponseEntity<ErrorResponse> handleFossaException(FossaException e) {
ErrorResponse error = new ErrorResponse(
"FOSSA_ERROR",
e.getMessage(),
Instant.now()
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
@ExceptionHandler(PolicyNotFoundException.class)
public ResponseEntity<ErrorResponse> handlePolicyNotFoundException(PolicyNotFoundException e) {
ErrorResponse error = new ErrorResponse(
"POLICY_NOT_FOUND",
e.getMessage(),
Instant.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(ComplianceGateException.class)
public ResponseEntity<ErrorResponse> handleComplianceGateException(ComplianceGateException e) {
ErrorResponse error = new ErrorResponse(
"COMPLIANCE_GATE_FAILED",
e.getMessage(),
Instant.now()
);
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
}
}
public class ErrorResponse {
private final String errorCode;
private final String message;
private final Instant timestamp;
public ErrorResponse(String errorCode, String message, Instant timestamp) {
this.errorCode = errorCode;
this.message = message;
this.timestamp = timestamp;
}
// Getters
}

Conclusion

FOSSA License Compliance in Java provides:

  1. Comprehensive License Management: Automated detection and analysis of open-source licenses
  2. Policy Enforcement: Configurable license policies with automated enforcement
  3. Build System Integration: Seamless integration with Maven and Gradle
  4. CI/CD Pipeline Integration: Automated compliance checks in Jenkins and other CI systems
  5. Advanced Reporting: Executive summaries, compliance dashboards, and detailed reports

Key benefits:

  • Risk Mitigation: Proactively identify and address license compliance issues
  • Automated Compliance: Integrate compliance checks into development workflows
  • Policy as Code: Define and manage license policies programmatically
  • Enterprise Ready: Scalable, configurable, and extensible architecture
  • Comprehensive Visibility: Detailed reporting and analytics for compliance posture

This integration enables organizations to maintain robust license compliance while accelerating software development through automation and policy-driven enforcement.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper