Skip to main content

Expanders 5.32.0

· 6 min read
Frédéric Hannes
Frédéric Hannes
R&D Engineer

In this release of Expanders, we've cleaned up some long-deprecated code that still uses UserContext as the main context transport object. There's also been several improvements in the infrastructure to improve ease of use for functional keys.


The expansion resources below provide Expanders 5.32.0.


Changes and improvements

Functional key infrastructure


A new class FinderResolver has been introduced, which is called from the Bean.find() method in the logic layer. This class will automatically resolve the DataRefs for any field operator pairs that target link fields. Resolving means that it will resolve the DataRef against the database in case there's no database identifier present in it. While much of the methods in the stack already did this, the finders still did not, which often caused some confusion. The finder would not work as intended if the database identifier was not present in the DataRefs. This resolution will only occur if there is no database identifier in the DataRef, so the impact on performance should be negligible in existing code.


The Agent.getDetails(DataRef) method did not behave quite the same as LocalAgent.getDetails(DataRef). Even though the same code was called down the line, the way it worked on the remote agent would lose all information in the DataRef except for the database identifier. This meant that it had to be resolved prior to use. This issue has now been resolved as well, by having it call getProjection() instead, just like its LocalAgent counterpart.

Context transport

In a distant past, there was no Context class, rather the only context was UserContext ans it was used to pass all context information. This has been replaced for years in favor of the Context class and the UserContext has since only been used for actual information about the user. Therefore, we've started removing a lot of this deprecated infrastructure that still relied on UserContext where Context should be used instead. Some of these methods include Agent.getAgent(UserContext) and LocalAgent.getLocalAgent().

Deprecating old methods

We intend to remove a lot of clutter that has been created in the stack over the years, with methods for backwards compatibility that should no longer be used. We've started marking many of these methods as deprecated for removal, so it is already clear to developers that these should not be used in new code and should be refactored if they are still in use in older code. A prime example of these are methods in the stack that still use a database identifier as a Long parameter, rather than a DataRef. We'll be marking more of these as deprecated in future releases.

Typed finders

The typed finders system is now the default for all applications. The option finders.enableTypedFinders is now no longer required. Typed finders means that generics are used through the stack for finders, to ensure that the compiler will verify that they are not used with infrastructure of the wrong DataElement. It also changes the FinderBean to use mapped method references for the finder implementations, rather than reflection. This means that it is no longer possible to defined custom finders that are not present in the model.

Migration guide

Migrating Agent.getDetails()

With Agent.getDetails() now behaving the same as LocalAgent.getDetails(), resolving the DataRef ahead of calling this is no longer required. This code can be cleaned up by removing the resolution step, certainly in the control layer where it will be less efficient to resolve from in most cases.

Migrating context transport

Migrating for the context transport changes should be trivial. All methods that were previously available with UserContext which were changed, now only take Context. For pretty much all of these, the Context alternative has been present for years. Code calling these should now simply use Context instead of UserContext. Some common examples include:

  • Passing parameterContext.getContext() instead parameterContext.getUserContext().
  • Using Context.from(userContext) in placed where only UserContext is available.

Any places where now only a Context object is available, but the UserContext itself is needed, it should be retrieved from the Context itself using Context.find(UserContext.class). This call will return an Optional<>, so the result can easily be handled in many ways. an empty UserContext was often used if no UserContext is available, so a possible refactor for that could be:

UserContext userContext = context.find(UserContext.class).orElse(UserContext.NO_USER_CONTEXT);

Agent constructors

Some constructors were removed from Agent and LocalAgent as well. These should not have been in use, but some of them were public. For all of these, static methods exist that will retrieve the agent if given a Context object.

An example would be LocalAgent(Local, UserContext) and LocalAgent(Local, UserContext, Context), which can be replaced by LocalAgent.getLocalAgent(context) (and LocalAgent.getLocalAgent(Context.from(userContext)) if only UserContext is available).

Struts actions

Some expanded struts actions had a dedicated getUserContext() method, which called UserContextRetriever.getUserContext() to obtain the UserContext. This method has been removed and there is no specific alternative for Context. This should be refactored by calling ContextRetriever.getContext() instead. This will return a Context object that contains the UserContext object as well.

This applies to the CommandPerformer, CommandPreparer, DetailsPreparer, FindAction, ModifyAction and ProjectorAction.

Deprecated agent methods

We added some deprecation of methods on the agents that take Long database identifiers instead of a DataRef. Though these will not be immediately removed, it would be prudent to refactor them anyway. This applies to the following methods currently:

  • Agent.getDataRef(Long)
  • Agent.getInfo(Long)
  • Agent.getDetails_old(Long)
  • Agent.getDetails(Long)

Since we wish to transition away from hardcoded methods for specific projections, it is also recommended to refactor Agent.getDetails(DataRef). The following example is a possible refactorings for Agent.getDetails(Long). Refactoring the other methods should follow the same pattern.

ProjectionRef projectionRef = new ProjectionRef()

Typed finders

Typed finders should in theory have little impact on existing implementations. The only exception is custom finders that were previously not part of the application model and relied on reflection to find the implementation. These finders should be added to the model as a new finder with the option isCustomFinder applied to it. This will provide an implementation stub in the FinderBean class, as well as a FindDetails class to which the existing custom implementation can be migrated.

A transient legacy option legacy.typedFinders.disable was introduced to revert to the old default behavior until 2024-03-01.