1. State of the API

With Structure 6, we are introducing a number of breaking changes to Java API. (REST API remains backwards compatible.) The changes are called for because of a large remake of the attributes subsystem in the product.

If you have an integration or an extension of Structure, your code might break. More specifically:

  • If you're creating your own attribute loaders, your code will definitely be incompatible and will need some work.
  • If you're just using the attribute subsystem, such as StructureAttributeService, your code may well break, but fixing it will most likely be trivial.
  • If you're using REST APIs or other parts of Structure API, your code is very unlikely to stop working.

2. Conceptual API Changes

2.1. Attribute Sensitivity Configuration

There are new global per-instance settings in Structure, called attribute sensitivity configuration. The administrator has access to them via Administration | Structure | Attributes.

These settings govern the situation where a total or some other type of aggregation is performed over a forest that the user does not have a full access to – there are items that they don't see.

StructureConfiguration interface now can be used to read or update these settings with getAttributeSensitivitySettings() / setAttributeSensitivitySettings().

2.2. Attribute Value Trails Removed

While the concept of trails remains, the API clients no longer have to deal with the trails. The clients are not supposed to implement their own cache – instead, they can request loading as frequently as needed, or use the Attribute Subscription Service.

2.3. Asynchronous Attribute Loading

AttributeSubscriptionService is now a preferred way to continuously receive updates on the attribute values for a particular set of rows and attributes. It is being used by the Structure grid and through REST APIs.

2.4. Support for Super-Root

We have introduced a concept of a "super-root", which is a virtual row above all the roots in the forest. It can be used to calculate a total across the whole forest, for example.

In order to support the super-root we had to reserve -1 as a special row ID number that identifies the super-root and only the super-root. It's now impossible to use -1 in Forest structures.

For more details, see SuperRootRow class.

3. Detailed API Changes

We list only some of the most important changes in this list. For a detailed description of the current API, see the javadocs.

Class NameBackwards-Incompatible ChangesOther Changes
AttributeValue
  • Moved to package c.a.j.s.api.attribute.
  • Trails are no longer a part of the value.
  • Stricter contract on nulls is defined.
StructureAttributeService
  • Method getAttributeValues() now returns RowValues instead of VersionedRowValues.
  • Method getAttributeValues() with both Forest and ForestSpec parameters is removed.
  • loadAttributeValues() methods, which push values through a receiver interface, are added.
  • getConsistentAttributeValues() methods are added.
  • getItemValues() method is added – it can load some attributes just for the item IDs, without a forest.
  • isItemAttribute() method helps with figuring out if an attribute is item-based or forest-based.
ValueFormat
  • Method cast(AttributeValue) is removed.

AttributeSpec
  • Method cast(AttributeLoader) is removed.
  • Attribute normalization rules are updated. Technically, it's a breaking change, but it should not affect custom attributes.
RowValues
  • getTrail() method removed.
  • consume() method added.
RestAttributeSpec
  • fromModel() method renamed to toRest().

ItemForestBuffer
  • Class is removed.

MigrationAwareSynchronizer
  • Class is removed.

SyncUtil
  • Class is removed.

4. Conceptual SPI Changes

You only need to bother with SPI if you extend Structure functionality by adding new attributes.

4.1. New Attribute Loader Hierarchy

With the new Attributes subsystem we've revamped and extended the types of attribute loaders / attributes that are supported. The new Structure now has support for the following attributes:

  • Item attributes are based only on an item and not on its position in the forest.
  • Derived attributes are calculated only based on the values of other attributes.
  • Single-row attributes are calculated based on the item and the row in the forest that this item is in.
  • Multi-row attributes are all based on the dependencies and a particular type of forest-based aggregation.
    • Aggregate attributes are based on the children values of the same attribute.
    • Propagate attributes are based on the ancestor value of the same attribute and sibling rows.
    • Scanning attributes are based on the value of a preceding row, without regard for the hierarchy and when the hierarchy is fully expanded.

For a more in-depth description, see the AttributeLoader javadoc page. Note that there's a parallel hierarchy of AttributeLoaderContext classes that has changed.

4.2. Attribute Caching

The new attribute subsystem is capable of caching attribute values in much more cases than the previous one. It is suggested that the implementations of attribute loaders avoid using AttributeCachingStrategy.MUST_NOT and review the AttributeLoader interface to see how the caching may be supported, for example, through context dependencies.

Also, we have introduced a new caching strategy – AttributeCachingStrategy.SHOULD_NOT, which is handy when the calculated object is heavy and should not be stored, but it allows caching of the dependent attributes.

4.3. Context Dependencies

The loaders may now declare their dependencies on some contextual values, for example, on user locale. This governs the caching and invalidation.

On the other hand, in order to call methods like AttributeLoaderContext.getLocale(), the loader must declare the context dependency.

4.4. Loader Builders

Item and single row loaders can now be built more conveniently with a number of builders. Start with AttributeLoaders class.

5. Detailed SPI Changes

We list only some of the most important changes in this list. For a detailed description of the current API, see the javadocs.

Class NameBackwards-Incompatible ChangesOther Changes
AttributeContext
  • Replaced method getBaseForestSpec() with getBaseStructureId()
  • Added getI18n() method
  • Added getTimezone() method
AttributeLoader
  • Replaced the sub-interfaces and their methods, including how values are loaded

AttributeLoaderProvider
  • createAttributeLoader method now accepts AttributeProviderContext instead of AttributeContext

BulkAttributeLoader
  • BulkAttributeLoader interface is eliminated. Every loader (except DerivedAttributeLoader) is now a bulk-loading loader.

AbstractDistinctAggregateLoader
  • Renamed to AbstractNaiveDistinctAggregateLoader

AttributeLoaderSecurity
  • Class removed: there's no need in supporting value security in the individual loaders anymore – the system does it by itself, with the sensitivity settings governing the multi-row loaders.

AbstractDistinctSumLoader
  • Distinct sum loaders are currently removed from the API.

SimpleAttributeProvider
  • Moved to package c.a.j.s.api.attribute.loader.basic.

TrailItemSet
  • Moved to package c.a.j.s.api.attribute.loader.

6. Effector APIs and SPIs

Structure 6.0 also adds a set of new APIs and SPIs related to effectors. This section only provides a basic overview, for details please refer to the Javadoc.

6.1. Managing Effectors

You need the EffectorInstanceManager to create, retrieve, update, and delete effector instances. To create an effector instance, you must provide a complete module key of an effector implementation and a map of parameters. Once you have the effector instance ID, pass it to CoreIdentities.effector(long) to get an item identity, which you'll be able to insert into a structure.

As of version 6.0, Structure provides only one effector implementation, the Attribute to Issue Field effector. Its module key is "com.almworks.jira.structure:effector-attribute-to-field".

6.2. Running Effectors

You need the EffectorProcessManager to run effectors. You start an effector process by calling startEffectorProcess(), which returns a process ID. You then use this process ID to track the status of your process, confirm effect application, and examine the results.

For a finished process, you can also start a new process that would undo the changes it made.

6.3. Writing a New Effector

As a Jira plugin developer, you can extend Structure with new effectors. Structure 6.0 adds two new extension points, effectors and effect providers. Effectors look at a forest and attribute values to generate effect descriptions (represented by StoredEffect). Effect providers validate effect descriptions and perform the requested changes.

This architecture supports code reuse. For example, Structure itself knows how to properly update most Jira issue fields, and supplies corresponding effect providers. So if all your effector does is update issue fields, you can use those providers and avoid writing issue-changing code yourself.

Effectors implement the Effector interface and are registered using the <structure-effector> module type. Your effector implementations can use the static factory methods in the CoreEffects class to generate effect descriptions that will be handled by Structure.

Effect providers implement the EffectProvider interface and are registered using the <structure-effect-provider> module type.