<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Component Developer Guide on Lucille</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/</link><description>Recent content in Component Developer Guide on Lucille</description><generator>Hugo</generator><language>en</language><lastBuildDate>Mon, 09 Jun 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/index.xml" rel="self" type="application/rss+xml"/><item><title>What the Framework Gives You</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/framework-benefits/</link><pubDate>Mon, 09 Jun 2025 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/framework-benefits/</guid><description>&lt;p&gt;When you are developing components for Lucille, it is important to understand what the framework does and does not give you. That understanding lets you take full advantage of what is already built, and gives you realistic expectations about the responsibilities that will fall to you as the implementor.&lt;/p&gt;
&lt;p&gt;This page begins with a general discussion of what you get when you adopt the framework overall. The following sections then focus on each component type — Stage, Connector, and Indexer. When you implement a component you will be extending the &lt;code&gt;Stage&lt;/code&gt; or &lt;code&gt;Indexer&lt;/code&gt; abstract classes, or implementing the &lt;code&gt;Connector&lt;/code&gt; interface. Each section explains how the framework interacts with that component, what the base class or framework infrastructure does for you automatically, and what you are responsible for writing yourself.&lt;/p&gt;</description></item><item><title>Developing New Components</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-new-components/</link><pubDate>Mon, 28 Oct 2024 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-new-components/</guid><description>&lt;p&gt;Each component type has its own dedicated guide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-stages/"&gt;Developing Stages&lt;/a&gt; — Skeleton, lifecycle, conditional execution, and the Document API.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-connectors/"&gt;Developing Connectors&lt;/a&gt; — Skeleton, lifecycle, and publishing documents.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-indexers/"&gt;Developing Indexers&lt;/a&gt; — Skeleton, lifecycle, and sending documents to a destination.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All components must declare a &lt;a href="https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/spec-validation/"&gt;SPEC&lt;/a&gt; and follow the &lt;a href="https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/javadocs/"&gt;Javadoc Standards&lt;/a&gt;. See &lt;a href="https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/testing/"&gt;Testing Pipelines&lt;/a&gt; for testing conventions.&lt;/p&gt;</description></item><item><title>Developing Stages</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-stages/</link><pubDate>Mon, 28 Oct 2024 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-stages/</guid><description>&lt;p&gt;To create a Stage, extend the abstract &lt;code&gt;Stage&lt;/code&gt; class and implement &lt;code&gt;processDocument()&lt;/code&gt;. That is the only method you are required to provide. The base class handles everything else: config validation, condition evaluation, metrics, thread isolation, and error routing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What the base class does for you:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Config validation&lt;/strong&gt; — The constructor calls &lt;code&gt;getSpec().validate(config)&lt;/code&gt; using your class&amp;rsquo;s &lt;code&gt;SPEC&lt;/code&gt; field. If the config has missing required properties, unknown properties, or type mismatches, validation fails at startup with a clear error message. You never call this yourself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Condition evaluation&lt;/strong&gt; — If the user configures &lt;code&gt;conditions&lt;/code&gt; on your stage, the base class evaluates them before calling &lt;code&gt;processDocument()&lt;/code&gt;. If conditions are not met, your method is never invoked for that document. You write &lt;code&gt;processDocument()&lt;/code&gt; as if conditions are always satisfied.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Thread isolation&lt;/strong&gt; — Each worker thread gets its own instance of your stage. Instance fields are effectively thread-local; no synchronization is needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metrics&lt;/strong&gt; — The base class tracks per-stage document count, latency, error count, and child count automatically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error handling&lt;/strong&gt; — If &lt;code&gt;processDocument()&lt;/code&gt; throws a &lt;code&gt;StageException&lt;/code&gt;, the framework catches it, marks the document as failed, and continues processing other documents.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What you implement:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Developing Connectors</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-connectors/</link><pubDate>Mon, 28 Oct 2024 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-connectors/</guid><description>&lt;p&gt;To create a Connector, extend &lt;code&gt;AbstractConnector&lt;/code&gt; and implement &lt;code&gt;execute()&lt;/code&gt;. That is the only method you are required to provide. The &lt;code&gt;execute()&lt;/code&gt; method receives a &lt;code&gt;Publisher&lt;/code&gt; — your connector reads from its data source and calls &lt;code&gt;publisher.publish(doc)&lt;/code&gt; for each document it produces. That&amp;rsquo;s the core job of a connector: create documents and publish them. The base class handles config validation, name/pipeline resolution, document ID prefixing, and provides no-op defaults for the optional lifecycle methods.&lt;/p&gt;</description></item><item><title>Developing Indexers</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-indexers/</link><pubDate>Mon, 28 Oct 2024 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-indexers/</guid><description>&lt;p&gt;To create an Indexer, extend the abstract &lt;code&gt;Indexer&lt;/code&gt; class and implement three methods: &lt;code&gt;sendToIndex()&lt;/code&gt;, &lt;code&gt;validateConnection()&lt;/code&gt;, and &lt;code&gt;closeConnection()&lt;/code&gt;. The base class manages the entire consumption loop — polling documents from the indexing queue, accumulating them into batches, flushing batches on size or timeout, sending completion/failure events, retrying transient failures, and logging throughput metrics.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Indexer&lt;/code&gt; is an abstract class (like &lt;code&gt;Stage&lt;/code&gt;) rather than an interface because the framework drives the indexer&amp;rsquo;s execution. The base class implements &lt;code&gt;Runnable&lt;/code&gt; and its &lt;code&gt;run()&lt;/code&gt; method contains the message consumption loop, batching logic, retry machinery, and event accounting. Your implementation only provides the transport-specific operations: validate the connection, send a batch, and close the connection.&lt;/p&gt;</description></item><item><title>Developing File Handlers</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-file-handlers/</link><pubDate>Fri, 23 May 2025 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-file-handlers/</guid><description>&lt;p&gt;A FileHandler turns a file&amp;rsquo;s content into Documents. Each implementation handles a specific file format — CSV, JSON, XML, or any custom format you need. The &lt;code&gt;FileConnector&lt;/code&gt; uses FileHandlers to parse files it discovers during traversal.&lt;/p&gt;
&lt;p&gt;To create a FileHandler, implement the &lt;code&gt;FileHandler&lt;/code&gt; interface. The framework handles discovery, instantiation, and integration with the FileConnector.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What the framework does for you:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discovery by file extension&lt;/strong&gt; — FileHandlers are mapped to file extensions in the connector&amp;rsquo;s &lt;code&gt;fileHandlers&lt;/code&gt; config block. The framework instantiates your handler and routes files to it based on their extension.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Config validation&lt;/strong&gt; — Your &lt;code&gt;getSpec()&lt;/code&gt; method declares legal properties; the framework validates the config at startup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;InputStream management&lt;/strong&gt; — The framework opens the file (from local disk, S3, Azure, GCS, or inside archives) and passes you an &lt;code&gt;InputStream&lt;/code&gt;. You don&amp;rsquo;t need to know where the file came from.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What you implement:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Developing Storage Clients</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-storage-clients/</link><pubDate>Fri, 23 May 2025 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/developing-storage-clients/</guid><description>&lt;p&gt;A StorageClient traverses a storage system, discovers files, and provides access to their content. The &lt;code&gt;FileConnector&lt;/code&gt; uses StorageClients to list and read files from local disk, S3, Azure Blob Storage, and Google Cloud Storage. If you need to read files from a storage system that Lucille doesn&amp;rsquo;t support out of the box (e.g., SFTP, SMB/CIFS, WebDAV), you implement a StorageClient.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; It is not currently possible to reference a custom StorageClient from a Lucille config. The mapping from URI scheme to StorageClient implementation is hardcoded in &lt;code&gt;StorageClient.create()&lt;/code&gt;. Adding a new storage backend today requires a PR to lucille-core — see the &lt;a href="https://kmwtechnology.github.io/lucille/docs-pre-release/contributing/"&gt;Contributor Guide&lt;/a&gt; for project structure and contribution workflow. Config-driven pluggable StorageClients are a roadmap feature. This page is included in the Component Developer Guide because the implementation pattern is the same regardless of how the client is registered.&lt;/p&gt;</description></item><item><title>SPEC Validation System</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/spec-validation/</link><pubDate>Mon, 09 Jun 2025 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/spec-validation/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;The SPEC system is Lucille&amp;rsquo;s configuration validation framework. It catches config errors — typos, missing required fields, wrong types — before any processing starts. Every Stage, Connector, and Indexer declares a &lt;code&gt;public static final Spec SPEC&lt;/code&gt; that defines what configuration properties it accepts.&lt;/p&gt;
&lt;p&gt;For example, this is how a Stage would declare that its config must contain a string property called &lt;code&gt;foo&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#204a87;font-weight:bold"&gt;public&lt;/span&gt;&lt;span style="color:#f8f8f8"&gt; &lt;/span&gt;&lt;span style="color:#204a87;font-weight:bold"&gt;static&lt;/span&gt;&lt;span style="color:#f8f8f8"&gt; &lt;/span&gt;&lt;span style="color:#204a87;font-weight:bold"&gt;final&lt;/span&gt;&lt;span style="color:#f8f8f8"&gt; &lt;/span&gt;&lt;span style="color:#000"&gt;Spec&lt;/span&gt;&lt;span style="color:#f8f8f8"&gt; &lt;/span&gt;&lt;span style="color:#000"&gt;SPEC&lt;/span&gt;&lt;span style="color:#f8f8f8"&gt; &lt;/span&gt;&lt;span style="color:#ce5c00;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#f8f8f8"&gt; &lt;/span&gt;&lt;span style="color:#000"&gt;SpecBuilder&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;.&lt;/span&gt;&lt;span style="color:#c4a000"&gt;stage&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;().&lt;/span&gt;&lt;span style="color:#c4a000"&gt;requiredString&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;(&lt;/span&gt;&lt;span style="color:#4e9a06"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;).&lt;/span&gt;&lt;span style="color:#c4a000"&gt;build&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;();&lt;/span&gt;&lt;span style="color:#f8f8f8"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The philosophy: fail loudly at startup, not silently at runtime. If a user&amp;rsquo;s config omits a required property or includes an unrecognized one (like a typo), Lucille reports the error before any documents are processed.&lt;/p&gt;</description></item><item><title>Quick Reference</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/quick-reference/</link><pubDate>Mon, 09 Jun 2025 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/quick-reference/</guid><description>&lt;p&gt;This page provides copy-paste-ready examples for the patterns developers use most frequently. It complements the detailed explanations in the Architecture and Contributing sections.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-threading-model"&gt;The Threading Model&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Each Worker thread creates its own Pipeline object, which creates its own instance of every Stage.&lt;/strong&gt; Instance fields in a Stage are effectively thread-local — use them freely without synchronization.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use a singleton:&lt;/strong&gt; If a resource is both expensive to initialize &lt;em&gt;and&lt;/em&gt; thread-safe for concurrent use, share it via a static field with lazy initialization. The existing &lt;code&gt;DictionaryManager&lt;/code&gt; class is the canonical pattern.&lt;/p&gt;</description></item><item><title>Testing Pipelines</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/testing/</link><pubDate>Mon, 09 Jun 2025 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/testing/</guid><description>&lt;p&gt;Lucille provides a first-class test mode that lets you run a complete pipeline end-to-end against real source data, without needing a running search backend. All documents, events, and messages are captured in memory and available for assertion after the run.&lt;/p&gt;
&lt;h2 id="runtypetest"&gt;RunType.TEST&lt;/h2&gt;
&lt;p&gt;When a run is started with &lt;code&gt;RunType.TEST&lt;/code&gt;, Lucille:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Runs all components (Connector, Workers, Indexer) as normal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bypasses the search backend&lt;/strong&gt; — no actual indexing occurs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Captures all messages&lt;/strong&gt; flowing between components in a &lt;code&gt;TestMessenger&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Returns a &lt;code&gt;RunResult&lt;/code&gt; containing the captured message history for assertions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="running-in-test-mode"&gt;Running in Test Mode&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;Runner.runInTestMode(config)&lt;/code&gt; or construct a &lt;code&gt;Runner&lt;/code&gt; with &lt;code&gt;RunType.TEST&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>Javadoc</title><link>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/javadocs/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kmwtechnology.github.io/lucille/docs-pre-release/developer-guide/javadocs/</guid><description>&lt;p&gt;This page covers two things: where to find the published Javadoc for the Lucille API, and how to write Javadoc on new Connectors, Stages, and Indexers so that the documentation tooling can parse and render their config parameters correctly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="published-api-reference"&gt;Published API Reference&lt;/h2&gt;
&lt;p&gt;The generated Javadoc for &lt;code&gt;lucille-core&lt;/code&gt; is published at:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://javadoc.io/doc/com.kmwllc/lucille-core/latest/index.html"&gt;javadoc.io/doc/com.kmwllc/lucille-core&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This covers all public classes, interfaces, and methods in the core library, including the &lt;code&gt;Document&lt;/code&gt;, &lt;code&gt;Stage&lt;/code&gt;, &lt;code&gt;Connector&lt;/code&gt;, &lt;code&gt;Indexer&lt;/code&gt;, and &lt;code&gt;Publisher&lt;/code&gt; APIs.&lt;/p&gt;</description></item></channel></rss>