LabelBuilderFactory in Linkis: Implementing Custom Label Builders for Engine Type Routing
LabelBuilderFactory in Linkis is the central interface that delegates label object creation to an ordered chain of ExtensibleLabelBuilder implementations, automatically discovered at runtime via classpath scanning to enable custom engine-type routing without modifying core code.
In Apache Linkis, labels are key-value pairs attached to job requests that drive routing decisions in the resource manager and engine selector. The LabelBuilderFactory decouples label instantiation from business logic, allowing you to inject custom routing rules by implementing the ExtensibleLabelBuilder interface.
Understanding the LabelBuilderFactory Architecture
Core Factory Interface and Default Implementation
The LabelBuilderFactory interface defines multiple createLabel method signatures for constructing labels from keys/values, input streams, or maps. According to the Apache Linkis source code in linkis-computation-governance/linkis-manager/linkis-label-common/src/main/java/org/apache/linkis/manager/label/builder/factory/LabelBuilderFactory.java, these methods form the public API used throughout the computation governance layer.
The default implementation, StdLabelBuilderFactory, maintains a LinkedList<ExtensibleLabelBuilder> and iterates over it during label creation. Located in StdLabelBuilderFactory.java, this class sorts builders by their getOrder() value, consulting each builder's canBuild method until it finds a match.
Automatic Builder Discovery via LabelBuilderFactoryContext
The LabelBuilderFactoryContext singleton manages factory lifecycle and builder registration. As implemented in LabelBuilderFactoryContext.java, it lazily instantiates the concrete factory class (defaulting to StdLabelBuilderFactory) and uses Reflections to scan the classpath for all ExtensibleLabelBuilder implementations during initialization. This zero-configuration approach means any custom builder packaged in the runtime is automatically registered.
The Builder Chain and Ordering Mechanism
The ExtensibleLabelBuilder interface in ExtensibleLabelBuilder.java specifies three critical methods: canBuild to determine capability, build to construct the label, and getOrder to establish precedence. Builders with lower order values are consulted first, allowing custom implementations to intercept label creation before the DefaultGlobalLabelBuilder (which uses Integer.MAX_VALUE) handles generic cases.
Implementing a Custom Label Builder for Engine Type Routing
To implement custom engine-type routing, extend AbstractGenericLabelBuilder and override the critical contract methods defined in ExtensibleLabelBuilder.java.
Step 1: Extend AbstractGenericLabelBuilder
Create a class that inherits from AbstractGenericLabelBuilder (found in AbstractGenericLabelBuilder.java). This base class provides default implementations for common builder operations, allowing you to focus on the routing logic.
Step 2: Implement canBuild and getOrder
Override canBuild(String key, Class<?> labelClass) to return true for your custom label keys. Set getOrder() to a value lower than Integer.MAX_VALUE (used by the default global builder) to ensure priority execution. This ordering mechanism is defined in ExtensibleLabelBuilder.java.
Step 3: Construct the EngineTypeLabel
Implement the build method to create EngineTypeLabel instances. The following Scala example demonstrates routing to a custom "spark-ml" engine when detecting a specific key:
// src/main/scala/com/example/linkis/engine/MlEngineLabelBuilder.scala
package com.example.linkis.engine
import org.apache.linkis.manager.label.builder.AbstractGenericLabelBuilder
import org.apache.linkis.manager.label.entity.engine.{EngineTypeLabel, EngineType}
import org.apache.linkis.manager.label.utils.LabelKeyConstant
class MlEngineLabelBuilder extends AbstractGenericLabelBuilder {
override def getOrder: Int = 10
override def canBuild(labelKey: String, labelClass: Class[_]): Boolean = {
labelKey == "mlEngine"
}
override def build(
labelKey: String,
valueObj: Object,
labelClass: Class[_],
valueTypes: java.lang.reflect.Type*
): EngineTypeLabel = {
val engineName = Option(valueObj).map(_.toString).getOrElse("spark")
val label = EngineTypeLabelCreator.createEngineTypeLabel(engineName)
valueObj match {
case m: java.util.Map[_, _] if m.containsKey("version") =>
label.setVersion(m.get("version").toString)
case _ =>
}
label
}
}
This builder intercepts the "mlEngine" key and delegates to EngineTypeLabelCreator (from EngineTypeLabelCreator.java) to construct the label, mirroring the pattern used in DefaultGlobalLabelBuilder.java.
Deployment and Runtime Integration
Package your builder in any module included in the Linkis runtime, such as a plugin JAR. The LabelBuilderFactoryContext.labelBuilderInitRegister method automatically discovers and registers the class via Reflections, requiring no XML configuration or SPI files.
When a client submits a job with the custom label:
{
"labels": [
{"labelKey": "mlEngine", "value": "spark"},
{"labelKey": "version", "value": "2.4.5"}
]
}
The factory iterates through the sorted builder chain, invokes your custom implementation, and produces an EngineTypeLabel that subsequent routing components treat as a standard engine type label.
Advanced Configuration Options
If you need to replace the entire factory implementation rather than adding a single builder, set the configuration key linkis.label.factory.class in LabelCommonConfig.java to your custom factory class name. This bypasses the default StdLabelBuilderFactory entirely.
Summary
- LabelBuilderFactory defines the contract for label creation in
LabelBuilderFactory.java, withStdLabelBuilderFactoryproviding the default ordered-chain implementation. - LabelBuilderFactoryContext handles automatic discovery of
ExtensibleLabelBuilderimplementations via classpath scanning at startup. - Extend AbstractGenericLabelBuilder to implement custom routing logic with minimal boilerplate.
- Override getOrder() to control precedence in the builder chain; lower values execute first.
- Set canBuild() to filter for specific label keys that trigger your custom engine-type mapping.
- No additional configuration files are required; the Reflections-based scan picks up new builders automatically.
Frequently Asked Questions
What is the difference between LabelBuilderFactory and LabelBuilderFactoryContext in Linkis?
LabelBuilderFactory is the interface defining label creation APIs, while LabelBuilderFactoryContext is the singleton that instantiates the concrete factory and manages the lifecycle of all builders. The context performs the classpath scan and registers builders with the factory instance.
How does Linkis determine which label builder to use for a specific key?
StdLabelBuilderFactory iterates over registered builders sorted by getOrder() value, calling canBuild(String key, Class<?> labelClass) on each until one returns true. The first matching builder wins the opportunity to construct the label.
Can I override the default engine type builder without modifying Linkis core code?
Yes. By implementing ExtensibleLabelBuilder with an order value lower than the default global builder (Integer.MAX_VALUE), your custom builder will be consulted first. When your canBuild method matches the engine type key, you control the label construction entirely.
Where should I place my custom label builder code so Linkis discovers it?
Package your class in any JAR included in the Linkis runtime classpath, such as a plugin module or linkis-engineplugin extension. The LabelBuilderFactoryContext uses Reflections to scan all packages for ExtensibleLabelBuilder implementations during initialization, so no explicit registration is required.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →