Configuring Java components
Any Java component might require some sort of configuration, be it simple strings or integers, or more complex structures. Because of all the boilerplate code that commonly goes into classes to hold such configuration, this often degenerates into a collection of key-value string pairs (e.g. javax.servlet.FilterConfig). To avoid this, Vespa has custom, type-safe configuration to all Container components. Get started with the Developer Guide, try the album-recommendation-searcher sample application.
Configurable components in short:
- The developer creates a config definition file - .def
- Use the Vespa bundle plugin to generate a config class from the definition
- Inject config objects in the application code
Config definition
Write a config definition
file and place it in the application's src/main/resources/configdefinitions/
directory. E.g. src/main/resources/configdefinitions/my-component.def
:
package=com.mydomain.mypackage myCode int default=42 myMessage string default=""
Generate config class
This is done in the bundle plugin: Generate Java config classes:
$ mvn generate-resourcesThis generates the config classes in
target/generated-sources/vespa-configgen-plugin/
.
In the above example, the config definition file was named my-component.def, and its package declaration is com.mydomain.myypackage. Hence, the full name of the generated java class is com.mydomain.mypackage.MyComponentConfig
It is a good idea to generate the config classes first, then resolve dependencies and compile in the IDE.
Use config in code
The generated config class is now available for the component through constructor injection, which means that the component can declare the generated class as one of its constructor arguments:
package com.mydomain.mypackage; public class MyComponent { private final int code; private final String message; @Inject public MyComponent(MyComponentConfig config) { code = config.myCode(); message = config.myMessage(); } }The Container will create and inject the config instance. To override the default values of the config, specify values in
src/main/application/services.xml
, like:
<container version="1.0"> <component id="com.mydomain.mypackage.MyComponent"> <config name="com.mydomain.mypackage.my-component"> <myCode>132</myCode> <myMessage>Hello, World!</myMessage> </config> </component> </container>and the deployed instance of
MyComponent
is constructed using a
corresponding instance of MyComponentConfig
.
Unit testing configurable components
The generated config class provides a builder API
that makes it easy to create config objects for unit testing.
Example that sets up a unit test for the MyComponent
class from the example above:
import static com.mydomain.mypackage.MyComponentConfig.*; public class MyComponentTest { @Test public void requireThatMyComponentGetsConfig() { MyComponentConfig.Builder builder = new MyComponentConfig.Builder(); builder.myCode(668) .myMessage("Neighbour of the beast"); MyComponentConfig config = new MyComponentConfig(builder); MyComponent component = new MyComponent(config); … } }The config class used here is very simple - see a separate example of building a complex configuration object.
Adding files to the component configuration
This section describes what to do if the component needs larger configuration objects that are stored in files, e.g. automata or large tables. Before proceeding, take a look at how to create provider components - instead of integrating large objects into e.g. a searcher or processor, it might be better to split the resource-demanding part of the component's configuration into a separate provider component. The procedure described below can be applied to any component type.
Files can be transferred using either file distribution
or URL download.
File distribution is used when the files are added to the application package.
If for some reason this is not convenient, e.g. due to size,
origin of file or update frequency, Vespa can download the file and make it available for the component.
Both types are set up in the config def file.
File distribution uses the path
config type, and URL downloading the url
type.
See the config file reference for details.
In the following example we will show the usage of both types.
Assume this config definition, named my-component.def
:
package=com.mydomain.mypackage myFile path myUrl urlThe file must reside in the application package, and the path (relative to the application package root) must be given in the component's configuration in
services.xml
:
<container version="1.0"> <component id="com.mydomain.mypackage.MyComponent"> <config name="com.mydomain.mypackage.my-component"> <myFile>my-files/my-file.txt</myFile> <myUrl>https://docs.vespa.ai/en/reference/query-api-reference.html</myUrl> </config> </component> </container>An example component that uses these files:
package com.mydomain.mypackage; import java.io.File; public class MyComponent { private final File fileFromFileDistribution; private final File fileFromUrlDownload; public MyComponent(MyComponentConfig config) { fileFromFileDistribution = config.myFile().toFile(); fileFromUrlDownload = config.myUrl(); } }The
myFile()
getter returns a java.nio.Path
object, while
the myUrl()
getter returns a java.io.File
object.
The container framework guarantees that these files are fully transferred or
downloaded and present at the given location before the component constructor is invoked.
Hence, the component can access the file contents right away.