skip to content

JAXP factories like DocumentBuilderFactory are Abstract Factories whose provider is pluggable. How does the runtime select the implementation, and what design forces does that pluggability serve?

level: seniorimportance: should knowfreq 40%

answer

  1. Order: system property → jaxp.properties → ServiceLoader → JDK default
  2. First match wins; newInstance(name,...) bypasses lookup
  3. Abstract Factory + service-provider mechanism
  4. Serves vendor independence / Open-Closed / testability
  5. Ambient lookup risk → configure secure processing explicitly (XXE)

basics

~20 s

newInstance() searches an ordered list — a system property, a jaxp.properties file, the service-loader (META-INF/services), then the built-in default — and uses the first provider it finds. This lets you swap XML libraries without changing code.

solid answer

~50 s

DocumentBuilderFactory.newInstance() uses the JAXP ordered provider-lookup to pick the concrete factory: it checks the javax.xml.parsers.DocumentBuilderFactory system property, then a jaxp.properties file in the JDK's conf directory, then the ServiceLoader mechanism (META-INF/services entries on the classpath/modulepath), and finally the JDK's built-in default implementation; the first match wins. This realizes the Abstract Factory's intent — the client depends only on the abstract factory and abstract products — but extends it with runtime substitutability of the entire provider. The design forces it serves: vendor independence (swap Xerces for another parser by config, not code), testability and policy control (you can inject a hardened/secure parser), and decoupling that respects the Open/Closed Principle. It also means a misconfigured property or a stray jar on the classpath can silently change which parser you get, so in security-sensitive contexts you configure the factory explicitly (features, secure processing) rather than trusting the ambient lookup.

go deeper

for a junior

Knows newInstance() picks an implementation for you and that you don't name the concrete parser class.

for a middle

Can describe that the JDK selects the provider via a lookup (property/services/default) and that this keeps client code decoupled.

for a senior

States the full ordered lookup, ties it to Abstract Factory + service-provider, and flags the security (XXE) and thread-safety consequences, configuring secure processing explicitly.

for a principal

Reasons about deployment-level pluggability vs. supply-chain risk (ambient service entries), module-system provides/uses, hardening policy across services, and when to bypass the lookup with an explicit provider for determinism.

## Abstract Factory + the service-provider mechanism JAXP (Java API for XML Processing) is the canonical place where the **Abstract Factory** pattern meets Java's **service-provider** (pluggability) mechanism. Understanding both halves is what makes this a senior-level topic. ### The lookup order (for DocumentBuilderFactory.newInstance()) When you call `DocumentBuilderFactory.newInstance()`, the runtime resolves the concrete factory by trying sources **in order** and taking the first that yields a class name: 1. **System property** — `javax.xml.parsers.DocumentBuilderFactory`. If set (e.g. via `-D` or `System.setProperty`), its value is the factory class to load. 2. **`jaxp.properties`** — a properties file in the JDK's `conf` directory (historically `lib/jaxp.properties`) containing the same key. Read once if the property wasn't set. 3. **`ServiceLoader` / `META-INF/services`** — the standard service-provider mechanism: a jar can ship `META-INF/services/javax.xml.parsers.DocumentBuilderFactory` naming its implementation. In the module system this corresponds to a `provides ... with ...` directive. The first provider found on the path is used. 4. **Built-in default** — if none of the above match, the JDK's bundled implementation is used. The overloaded `newInstance(String factoryClassName, ClassLoader)` bypasses the lookup and loads the named class directly. ### Why this is still Abstract Factory The pattern's essence is unchanged: the client holds only `DocumentBuilderFactory` (abstract factory) and `DocumentBuilder`/DOM types (abstract products), and never names a concrete class. Pluggability is an *addition*: the concrete factory is chosen at runtime by the lookup instead of being passed in by a caller. So JAXP is Abstract Factory **plus** the service-provider idiom. ### Design forces the pluggability serves - **Vendor/library independence.** You can replace the XML parser implementation (different performance, standards support, or bug profile) by configuration alone, never editing client code. This is the Dependency Inversion / Open-Closed payoff at the deployment level. - **Layered defaults with override points.** Most apps use the JDK default; a deployment can override globally (property/file) or a library can override locally (its own service entry). Override granularity matches the operational need. - **Policy and hardening.** You can substitute a parser configured for secure processing, or one instrumented for a platform. - **Testability.** Tests can force a specific or fake provider. ### The risks a senior should flag - **Ambient configuration is fragile.** A stray jar on the classpath shipping a `META-INF/services` entry can silently change which parser your app uses — a classic 'works on my machine' / dependency-conflict bug. - **Security: XXE.** XML parsers default to features that can be dangerous (external entity resolution → XXE attacks, billion-laughs entity expansion). Because the *which-parser* decision is ambient, you should not rely on it for safety. Instead, **explicitly configure the factory** after obtaining it — e.g. enable `XMLConstants.FEATURE_SECURE_PROCESSING`, disable DOCTYPE/external general+parameter entities — regardless of which provider the lookup returned. The Abstract Factory gives you a configured *factory* object precisely so you can set these features in one place before creating products. - **Thread-safety.** Factory instances and the builders they create are generally **not** thread-safe; create per-use or pool carefully. This is an operational consequence of the pattern's object-per-family design. ### The senior framing Abstract Factory decouples client from concrete product; the JAXP service-provider lookup decouples *deployment* from concrete provider. Together they give pluggability — but pluggability via ambient lookup is a double-edged sword: great for vendor independence, dangerous if you assume the wrong provider/features. The mature stance is: let the lookup choose the implementation, but **explicitly configure security-relevant features on the factory yourself**.

  • Why shouldn't you rely on the ambient provider lookup for security?
    Because which provider you get can be changed by a system property, a jaxp.properties file, or any jar shipping a META-INF/services entry — none of which you fully control at deploy time. XML security (XXE, entity-expansion) must be set explicitly on the factory you obtain (e.g. FEATURE_SECURE_PROCESSING, disabling DOCTYPE/external entities), independent of which implementation was selected.
  • Are DocumentBuilderFactory and DocumentBuilder thread-safe?
    Generally no. A DocumentBuilder is not safe for concurrent use, and factories aren't guaranteed thread-safe either. Create or pool them per thread/per use rather than sharing one builder across threads.

saying these in an interview costs you the question

  • Saying the provider is fixed/hardcoded — it's resolved by an ordered runtime lookup.
  • Believing the default parser is safe out of the box — XXE/entity-expansion must be disabled explicitly.
  • Assuming factories/builders are thread-safe and sharing one across threads.
  • Ignoring that a classpath jar can silently override which provider is used.

context