Fork me on GitHub

Using the OSCAL Binding Context

This guide explains how to initialize and use the OscalBindingContext, which is the foundation for all OSCAL operations in liboscal-java.

Before you can read, write, or validate OSCAL documents, you need a binding context. The binding context is the central object that:

  • Knows about all OSCAL document types (Catalog, Profile, SSP, etc.)
  • Creates deserializers for reading OSCAL files
  • Creates serializers for writing OSCAL files
  • Provides access to Metapath evaluation
  • Handles constraint validation

Think of it as the “factory” that produces all the tools you need for OSCAL processing. In liboscal-java, the OscalBindingContext extends the base Metaschema binding context with OSCAL-specific configuration.

The OscalBindingContext is the central entry point for working with OSCAL in liboscal-java. It provides:

  • Pre-configured access to all OSCAL model types
  • Serialization and deserialization capabilities
  • Metapath expression support
  • Constraint validation

For most use cases, use the singleton instance:

import dev.metaschema.oscal.lib.OscalBindingContext;

OscalBindingContext context = OscalBindingContext.instance();

The OscalBindingContext.instance() method returns a pre-configured context that:

  • Loads all OSCAL Metaschema modules
  • Registers OSCAL-specific Metapath functions
  • Is thread-safe for concurrent use
  • Avoids repeated module loading overhead

Once you have the context, you can work with any OSCAL document type:

import dev.metaschema.oscal.lib.model.Catalog;
import dev.metaschema.oscal.lib.model.Profile;
import dev.metaschema.oscal.lib.model.SystemSecurityPlan;

// Create deserializers
IDeserializer<Catalog> catalogReader = context.newDeserializer(
    Format.JSON, Catalog.class);
IDeserializer<Profile> profileReader = context.newDeserializer(
    Format.XML, Profile.class);
IDeserializer<SystemSecurityPlan> sspReader = context.newDeserializer(
    Format.JSON, SystemSecurityPlan.class);

The binding context provides access to all OSCAL document types. Each type corresponds to a root element in the OSCAL specification:

Class Description
Catalog OSCAL catalogs with controls and groups
Profile Control selection and tailoring
MappingCollection Control mapping between frameworks
SystemSecurityPlan System security documentation
ComponentDefinition Reusable component capabilities
AssessmentPlan Security assessment planning
AssessmentResults Assessment findings
PlanOfActionAndMilestones POA&M tracking

Understanding how OSCAL elements map to Java classes helps you navigate the API. The generated classes mirror the OSCAL model structure:

Each OSCAL document type has a corresponding Java class with nested classes for its components:

Catalog (root)
├── Metadata              # Document metadata (title, dates, parties)
│   ├── Property          # Key-value properties
│   └── Link              # Related resources
├── Group                 # Control groupings
│   └── Control           # Individual controls
│       ├── Parameter     # Control parameters
│       ├── Part          # Prose sections
│       └── Property      # Control properties
└── BackMatter            # Referenced resources
    └── Resource          # External documents

OSCAL uses consistent patterns across document types. Once you understand these patterns, working with any OSCAL type becomes intuitive:

OSCAL Concept Java Pattern Example
Root element Top-level class Catalog, Profile
Nested elements Nested classes Catalog.Group, Control.Part
Required UUID getUuid() method catalog.getUuid()
Metadata getMetadata() method catalog.getMetadata()
Back matter getBackMatter() method catalog.getBackMatter()
Properties getProps() method Returns List<Property>
Links getLinks() method Returns List<Link>

Use getter methods to traverse the document structure:

Catalog catalog = deserializer.deserialize(path);

// Access metadata
Metadata metadata = catalog.getMetadata();
String title = metadata.getTitle().toString();

// Iterate through groups and controls
for (Group group : catalog.getGroups()) {
    System.out.println("Group: " + group.getTitle());

    for (Control control : group.getControls()) {
        System.out.println("  Control: " + control.getId() + " - " + control.getTitle());
    }
}

Create OSCAL documents programmatically using constructors and setters:

Catalog catalog = new Catalog();
catalog.setUuid(UUID.randomUUID());

Metadata metadata = new Metadata();
metadata.setTitle(MarkupLine.fromMarkdown("My Catalog"));
metadata.setLastModified(ZonedDateTime.now());
metadata.setVersion("1.0.0");
catalog.setMetadata(metadata);
OscalBindingContext
    └── Extends BindingContext (from metaschema-databind)
         └── Loads OSCAL Metaschema modules
              ├── oscal_catalog
              ├── oscal_profile
              ├── oscal_ssp
              ├── oscal_component-definition
              ├── oscal_assessment-plan
              ├── oscal_assessment-results
              └── oscal_poam

For advanced use cases, you can access the underlying Metaschema:

import dev.metaschema.core.model.IModule;

// Get the OSCAL module
IModule oscalModule = context.getModuleByUri(
    URI.create("http://csrc.nist.gov/ns/oscal/1.0"));

The OscalBindingContext.instance() is:

  • Thread-safe for reading operations
  • Immutable once created
  • Suitable for use in multi-threaded applications
// Safe to use from multiple threads
ExecutorService executor = Executors.newFixedThreadPool(4);
OscalBindingContext context = OscalBindingContext.instance();

for (Path file : files) {
    executor.submit(() -> {
        // Each thread can safely use the same context
        Catalog catalog = loadCatalog(context, file);
        processCatalog(catalog);
    });
}

Handle module loading errors appropriately:

try {
    OscalBindingContext context = OscalBindingContext.instance();
} catch (MetaschemaException e) {
    // Handle module loading failure
    logger.error("Failed to load OSCAL modules", e);
    throw new RuntimeException("OSCAL initialization failed", e);
}
  1. Use the singleton - Avoid creating multiple context instances
  2. Reuse the context - Pass the same context throughout your application
  3. Handle errors - Catch and handle MetaschemaException appropriately
  4. Don't cache deserializers - Create new deserializers as needed; they're lightweight

Continue learning about liboscal-java with these related guides: