Storing Plugin State and Settings
Most of the plugins need a location to store their state and settings.
By state we mean the internal plugin data which cannot be easily computed from scratch and is usually not visible to the users of the plugin.
The state can be either global or associated with some TeamCity entity, such as a build or a build configuration.
By settings we understand user-defined settings, specified either via the user interface or DSL.
The plugin settings can be global or associated with TeamCity entities, such as a build configuration or a project.
Usually both the state and settings should survive the server restart.
In most cases plugin settings will be serialized and deserialized automatically by the TeamCity server itself; however, to serialize the state, some dedicated code should be provided by the plugin author, except for the cases when the plugin state can be calculated based on other TeamCity entities.
For instance, a plugin whose state depends on build tests can restore its state during the server startup by processing recent builds.
In TeamCity it is possible for a plugin to define its settings on a global level. These settings can be stored on a disk in the xml or some other plugin-specific format. The traditional place for storing all global settings is <TeamCity Data Directory>/config
.
TeamCity does not provide an API to serialize or deserialize settings under the config
directory. It's up to the plugin author to write this code.
To provide a user interface to edit global settings, a plugin can define a custom tab in the Administration area by implementing the AdminPage
extension.
See also Web UI Extensions.
note
In many cases, instead of having settings on a global level, it is better to associate settings with a project.
Since every TeamCity installation always has <Root project> which is the top of the projects' hierarchy, defining settings at the <Root project> level is essentially the same as defining them globally.
To associate settings with a project, a plugin can use SProjectFeatureDescriptor
.
A project feature is a map of parameters of type String
with some id
and some type
unique among all plugins. If plugin settings can be represented as a map, then project features provide a convenient way of associating plugin settings with a project or project hierarchy. In this case, there is no need to think about serialization or deserialization of settings: they will be saved and restored automatically when the project itself is saved or restored.
Project features can be added/removed at any given point of time with the help of the following methods:
addFeature
: this method will createSProjectFeatureDescriptor
instance and assign an ID to it.
Note: changes won't be stored on the disk automatically, thepersist
method must be called to save the project settings on the disk.removeFeature
: this method can be used to remove feature from a project, as with theaddFeature
method,persist
must be called to save the settings on the disk.
There are also convenience methods updateFeature
to update feature settings without recreating them, as well as different search methods:
note
There is an important distinction between the so-called `own` and `available` project features.
Since project features often affect not only the project where they are defined but all subprojects too, it is convenient to have a method returning the project features of a current project and all its parents.
This is how
getAvailableFeatures*
methods work. The features of the current project will be the first in the collection, then there will be the features of its parent, and so on until <Root project> is reached.On the contrary,
getOwnFeatures*
methods will return the features of the current project only.
To provide a user interface for editing project feature settings, a plugin can define a custom tab by implementing the EditProjectTab
extension.
See also Web UI Extensions and TeamCity Invitations plugin.
Sometimes plugin settings are represented as files. For instance, SSH keys or Maven settings are just files and ideally they should be stored as such.
TeamCity provides a directory for each project where files associated with the project can be stored.
Use the getPluginDataDirectory
method to access this directory.
The directory location on the disk is: <
TeamCity Data Directory>/config
/<Project external ID>/pluginData/<plugin name>
. It's up to the plugin author to write code to store/remove/modify and delete files under this directory. TeamCity does not provide additional API in this case.
If a plugin needs to associate some settings with a build configuration or template, then it should use Build Features.
In this case, similarly to project features, serialization and deserialization of settings will be performed automatically when a build configuration or template is saved on the disk or restored.
If a plugin needs to save some state globally, then it should be stored under the <TeamCity Data Directory>/system/
pluginData/<plugin name>
directory.
A plugin is free to use any serialization mechanism to store its state; TeamCity itself does not provide common utilities to save or access it.
To access the directory containing state of all plugins, use the ServerPaths#getPluginDataDirectory
method where ServerPaths
is a Spring bean.
Both SProject
and SBuildType
provide access to CustomDataStorage
.
Custom data storage is a key-value storage with some id
. Any map with String
keys and values can be stored there. There are no restrictions on the amount of data or length of keys and values, although it is not recommended to store megabytes of data there as it can affect the server performance and memory usage.
An example how a custom data storage can be used with a build configuration (code is similar in case of project):
SBuldType bt = ...
CustomDataStorage pluginData = bt.getCustomDataStorage("my-plugin-data");
pluginData.putValue("some key", "some value");
// optionally persist state
pluginData.flush()
Custom data storage is persisted into the database automatically with some periodicity or when server is stopped. But if it is important to save the state as soon as possible, then the flush
method can be used.
To remove custom data storage, use the dispose
method.
If a plugin needs to associate some state with a build, a possible approach is to store the state under the build artifacts directory on the disk. To access this directory, use the SBuild#getArtifactsDirectory
method.
By convention, plugins should store their state under the hidden artifacts directory .teamcity/<plugin name>
:
SBuild build = ...
File artifactsDir = build.getArtifactsDirectory();
File pluginFolder = new File(artifactsDir, jetbrains.buildServer.ArtifactsConstants.TEAMCITY_ARTIFACTS_DIR + File.separatorChar + "myPluginName");
pluginFolder.mkdirs();
... some code to serialize or deserialize state ...
note
Plugin can also use `CustomDataStorage` from [`SBuildType`](http://javadoc.jetbrains.net/teamcity/openapi/current/jetbrains/buildServer/serverSide/SBuildType.html) to store data associated with a build. This approach requires a proper cleanup code to ensure that the storage will be removed as soon as it is no longer required: either when the build finishes or when the build is removed. If the state is stored in the build artifacts, TeamCity will clean it up automatically when removing the build from the history.
Thanks for your feedback!