Expanders 5.32.0
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.
Resources
The expansion resources below provide Expanders 5.32.0.
| Resource | Version |
|---|---|
| Expanders | 5.32.0 |
| nsx-default-stack | 2023.15.2 |
| rest-jaxrs-stack | 3.27.0 |
Changes and improvements
Functional key infrastructure
FinderResolver
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.
Agent.getDetails()
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()insteadparameterContext.getUserContext(). - Using
Context.from(userContext)in placed where onlyUserContextis 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.
agent.getDetails(databaseId)
ProjectionRef projectionRef = new ProjectionRef()
.setDataRef(DataRef.from(databaseId))
.setProjection("details");
agent.getProjection(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.
