Migration guide: platform 2.0.x to 2.1.x
Platform 2.1.0 is bound to Across 3.0.0, which is the first release with the
across-autoconfigure
artifact and builds more on top of Spring Boot. Upgrading to Platform 2.1.0 usually requires you to make some configuration and code changes to your applications.
This guide is not a full overview of all new features and changes in Across or any of its modules. It mainly focuses on upgrade related changes: usually breaking changes or new features that impact application/module configuration.
Across Core
Auto-configuration support
Add autoconfigure artifact
When upgrading your applications (built with @AcrossApplication
), the first step is to add the new across-autoconfigure
artifact to your project.
<dependencies>
<dependency>
<groupId>com.foreach.across</groupId>
<artifactId>across-autoconfigure</artifactId>
</dependency>
</dependencies>
The new autoconfigure artifact contains compatibility configuration for many Spring Boot starters. Adding it to your project allows you to rely more on the starters and to remove some previously required configuration imports.
Please see the Spring Boot compatibility documentation for a detailed list of starters that are now supported.
Some useful additions now available without additional configuration:
-
Websocket support (built-in with AcrossWebModule)
-
Spring Data web support
-
Actuator support
-
Spring Boot Admin (& client) support
-
Spring Session support
-
Swagger2 support
When adding the across autoconfigurate artifact to your application, Spring Data Web support is enabled, which causes all repositories to be exposed as well. |
Cleanup @AcrossApplication
Review the release notes and Spring Boot compatibility documentation and remove imports and annotations from your @AcrossApplication
class.
// Across Platform 2.0.x
@AcrossApplication(
modules = { ... },
modulePackages = { "my.custom.modules" },
modulePackageClasses = { AcrossPlatform.class } (1)
)
@EnableSpringDataWebSupport (2)
@Import( DataSourceAutoConfiguration.class )
public class MyApplication
{
}
// Across Platform 2.1.x - with across-autoconfigure
@AcrossApplication( modules = { ... } )
public class MyApplication
{
}
1 | It is no longer necessary to include the default Across standard modules packages when you want to add your own packages for module scanning. The default standard modules package will always be included. |
2 | Several annotations and configuration imports can simply be dropped as across-autoconfigure takes care of the configuration now. |
If you have any of the following annotations in any of your modules or on your application class, they can just be removed when you have the right starters on the classpath:
-
@EnableSpringDataWebSupport
-
@EnableWebSocket
-
@EnableWebSocketMessageBroker
META-INF/across.configuration
Across 3.0.0 externalizes some of its configuration in a META-INF/across.configuration
properties file.
This is especially used for auto-configuration and configuration validation settings.
Please see the Across reference documentation section for a full description of the configuration file and what you can do with it.
Configuration validation
Across now attempts to validate your configuration when starting. It will check for the presence of configuration classes in places where they should not be. This could be using a configuration class on either the application level or in a module, where a different module already handles that particular bit of configuration.
Bootstrapping your application with a bad configuration will fail.
One of the additions in Across 3.0.0 is the new failure analyzers.
When you are using @AcrossApplication
and executing as a Spring Boot application, you should receive a more readable error messages with specific information on the problem.
An example is that bootstrapping will fail if a @ComponentScan
is detected on your @AcrossApplication
class, where that component scan would conflict with the application module package.
It is possible that in certain applications this would not cause an actual problem, it is however still considered a bad practice and an invalid configuration.
You can disable configuration validation - for example in production - by setting the property across.configuration.validate
to false
.
We strongly advise you to leave it on during development mode as it can help you avoid common configuration mistakes.
It’s possible to configure your own illegal configurations in the META-INF/across.configuration
file.
This is especially useful for shared modules that activate application-level features.
Multiple datasources
If your application has multiple datasources, it is now required that one of them is marked as @Primary
.
It is best to have a specific datasoure named acrossDataSource
as well to mark the datasource that should be used for the core Across schema and the standard modules.
Though not strictly required, you usually want to mark the acrossDataSource
as the @Primary
datasource.
@Bean
@Primary
@ConfigurationProperties("app.datasource.across")
public DataSourceProperties acrossDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
@ConfigurationProperties("app.datasource.across")
public DataSource acrossDataSource() {
return acrossDataSourceProperties().initializeDataSourceBuilder().build();
}
@Bean
@ConfigurationProperties("app.datasource.bar")
public DataSourceProperties barDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@ConfigurationProperties("app.datasource.bar")
public DataSource barDataSource() {
return barDataSourceProperties().initializeDataSourceBuilder().build();
}
New event handling
The internal event handling system has been entirely rewritten and now builds on top of the default Spring Framework event handling infrastructure.
Instead of the Across-specific @Event
, the Spring Framework @EventListener
should now be used.
Apart from how events are treated internally, there is no difference anymore in handling Across specific or regular Spring Framework events.
Most old event related classes have simply been deprecated, and breaking changes have been kept as limited as possible. In simple applications no changes would be required for this release.
// Across Platform 2.0.x
@Event
public void handle( @EventName("eventName") MyEvent myEvent ) {
}
// Across Platform 2.1.x
@EventListener(condition = "#myEvent.eventName == 'myEvent'")
public void handle( MyEvent myEvent ) {
// Called if the event is of type MyEvent
// and property 'eventName' has the value 'myEvent'
}
See the deprecations section for a list of the event-related deprecations and how to replace them.
Generic event types are handled differently with the new system, where in the past SomeEvent<Object> would have worked, the only working (and more correct) signature is now SomeEvent<? extends Object> .
|
Due to this refactoring some new features are now also available:
-
using
@TransactionalEventListener
-
explicitly order individual event handler methods using
@Order
or@OrderInModule
-
event listener methods can have a return value which will be published as a new event
Please read the Across reference documentation section for a full overview of all features.
The MBassador dependency - responsible for the previous event bus implementation - has been removed entirely.
Any listener methods that were directly using MBassador annotations will have to be rewritten as an @EventListener
.
Across bootstrap events
One of the breaking changes with the new event handling infrastructure is that it is no longer possible to use regular @Event
(or @EventListener
) to intercept Across bootstrap related events from the parent application class.
If you want to handle bootstrap related events from a parent ApplicationContext
, you should do so in a component implementing the AcrossLifecycleListener
interface.
Using AcrossLifecycleListener to modify the Across context configuration from a parent ApplicationContext (eg the @AcrossApplication class) is not required.
Instead you can use the AcrossBootstrapConfigurer interface on one ore more of your components, which was introduced especially for this purpose.
|
@ModuleConfiguration
The internal processing of @ModuleConfiguration
classes - module extensions provided by other modules - has been changed.
This was necessary to fix issues with incorrect handling of conditionals on these @ModuleConfiguration
classes.
This introduces some possibly breaking changes:
-
@ModuleConfiguration
classes are now always@Configuration
classes as well, this means they would also be processed by regular component scans, where in the past they might not have been -
@ModuleConfiguration
classes are only scanned for in theextensions
package, any@ModuleConfiguration
class in aconfig
package will now be loaded in the original module, instead of correctly treated as a module configuration extension -
import order has changed: module extensions will now always be imported before any other annotated classes (and before any injected Spring Boot auto-configuration classes)
New conditionals
Across 3.0.0 introduces some new conditionals:
@ConditionalOnAcrossModule
-
Use this annotation when you want to check for the presence or absence of a specific Across module. This replaces the use of
@AcrossDepends
on regular components.@AcrossDepends
should now only be used on module descriptors, and you will see a warning printed when it is used on components. @ConditionalOnAutoConfiguration
-
Use this conditional if you want to check if an auto-configuration class has been loaded anywhere in the Across application.
AcrossModule component scan
The default AcrossModule
still only scans the config
package by default.
However, a very common case is the behaviour of the dynamic modules: all child packages are scanned for components, with the exception of extensions
and installers
.
The former contains components for module extensions, the latter installers and related components.
A new helper method has been added to quickly configure an Across module with this behaviour.
MyModule extends AcrossModule {
public static final String NAME = "MyModule";
@Override
public String getName() {
return NAME;
}
@Override
protected void registerDefaultApplicationContextConfigurers( Set<ApplicationContextConfigurer> contextConfigurers ) {
contextConfigurers.add( ComponentScanConfigurer.forAcrossModule( MyModule.class ) );
}
}
Deprecations
The following is a list of classes that have been deprecated in Across 3.0.0:
StringToDateConverter
-
A replacement implementation
StringToDateTimeConverter
is available that also supports the Java 8 time implementations. AcrossEvent
-
It is not required anymore for an event type to implement this marker interface.
@Event
-
Use the Spring Framework
@EventListener
instead. @EventName
-
Use a
conditional
attribute on your@EventListener
instead. ParameterizedAcrossEvent
-
Implement the Spring Framework
ResolvableTypeProvider
interface instead. AcrossEventPublisher
-
Use the Spring Framework
ApplicationEventPublisher
instead.
Across Web
This chapter lists some important - possibly breaking - changes in Across Web support (provided by AcrossWebModule).
Interceptor configuration
PrefixingHandlerMappingConfigurer
can no longer be applied to the default AcrossWebModule, use a regular WebMvcConfigurer
to add interceptors instead.
-
different prefixed handler mappings (eg. AdminWebModule) still require the use of
PrefixingHandlerMappingConfigurer
for adding interceptors -
if you want to add an interceptor to all controllers, you should implement both
WebMvcConfigurer
andPrefixingHandlerMappingConfigurer
Menu building
The menu infrastructure support has been optimized to fix some technical and functional issues with the moving of path based menu items. Some code has been deprecated and still works as before, alongside the addition of new improved features.
Most important (breaking) changes:
-
MenuSelector
factory methods have been moved from theMenu
toMenuSelector
class -
some rarely used methods on
PathBasedMenuBuilder
,PathBasedMenuItemBuilder
andBuildMenuEvent
have been removed -
BuildMenuFinishedEvent
has been removed, modules requiring this functionality should now register aMenu
post-processor on theBuildMenuEvent
instead -
renamed methods on
BuildMenuEvent
:-
setSelector()
→setMenuSelector()
,getSelector()
→getMenuSelector()
-
forMenu()
→isForMenuOfType()
-
the unreliable
move()
method for menu items has been deprecated, use the newchangeItemPath()
oritem().changePathTo()
instead
-
The reference documentation regarding menu building has been rewritten completely, we strongly advise you to read the documentation and update your code accordingly.
Multipart uploads
The MultipartProperties
from Spring Boot are now being used for multipart configuration.
This means different property names should be used for custom configuration.
-
replace
acrossWebModule.multipart.auto-configure
withspring.http.multipart.enabled
-
replace
acrossWebModule.multipart.settings.*
with the equivalentspring.http.multipart.*
properties
Some default settings - for example maximum file size - might have changed as well.
spring:
http:
multipart:
max-file-size: 10MB
Across Standard Modules
The following section applies to the breaking changes in the standard modules. These instructions are only relevant if you use these modules in your applications.
AcrossHibernateJpaModule
The module has undergone significant changes to increase compatibility with the Spring Data JPA starter. When plugging a single AcrossHibernateJpaModule in an application it will now transparently take over the starter JPA support on the application module.
Most important changes include:
-
the default physical naming strategy being used by Hibernate has changed from a Hibernate default to a Spring Boot default
-
this can cause generated queries to be wrong (for example when having a reserved keyword as a table name)
-
in case of problems, you can revert to the old strategy by setting
acrossHibernate.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
-
-
a
PlatformTransactionManager
is now always created-
the configuration property to disable the
PlatformTransactionManager
has been removed
-
-
in addition a
TransactionTemplate
bean is now also created and exposed -
it’s now also possible to specify packages to scan for entities by injecting an
@EntityScan
annotated class into the AcrossHibernateJpaModule -
the default AcrossHibernateJpaModule will expose its relevant beans as primary unless specified otherwise
-
any module can take over the primary role by setting
acrossHibernate.primary=false
andmyModule.primary=true
-
the primary module will also create a
transactionManager
andtransationTemplate
alias for the corresponding beans it exposes
-
See the AcrossHibernateJpaModule documentation for a full list of release notes.
SpringSecurityModule
SpringSecurityModule now re-uses Spring Boot security configuration and inspects SecurityProperties
for default settings.
As a result, additional security might be activated in your application, where previously it was not (see below).
There are now more security filters applied, possibly with a different ordering than before. It is advised to review and test the security configuration of your applications, to ensure it still works as expected.
It is now possible to activate basic authentication for an entire application by setting security.basic.enabled
to true
.
Unlike a standard Spring Boot application, this is not enabled by default in an Across application.
The main reason for the latter is to ensure backwards compatibility with previous releases.
The different SecurityProperties
allow you to configure a default user, password and role.
Any class wanting to change the AuthenticationManagerBuilder
must now be annotated with @EnableGlobalAuthentication
.
Configuration registered as module extension in SpringSecurityModule and simply auto-wiring the AuthenticationManagerBuilder
will no longer work.
@ModuleConfiguration(SpringSecurityModule.NAME)
@EnableGlobalAuthentication // required
public class AuthenticationConfiguration
{
@Autowired
public void configureGlobal( AuthenticationManagerBuilder auth ) throws Exception {
auth.inMemoryAuthentication()
.withUser( "admin" ).password( "admin" )
.authorities( new SimpleGrantedAuthority( "access administration" ) );
}
}
Actuator endpoints are now always secured by default, unless you explicitly set management.security.enabled
to false
.
Previously the security configuration for Actuator was simply ignored, whereas now it should be applied as expected.
The H2 Web Console (usually /h2-console
) will now be secured by default, unless you explicitly set security.basic.enabled
to false
.
See the SpringSecurityModule documentation for a full list of release notes.
BootstrapUiModule
BootstrapUiModule has mainly been updated with new features. The following are the most important updates from a migration point of view:
Direct use of BootstrapUiFactory
or BootstrapUiComponentFactory
is now discouraged, these interfaces have been deprecated.
Use the stateless BootstrapUiBuilders
facade instead.
FormGroupElement
has been refactored to support more descriptions:
-
tooltip
can be set which will be added to the label - after the label text and required indicator -
a
descriptionBlock
can be set which will be added to the group before the control -
a
helpBlock
can be set which will be added to the group after the control
The property renderHelpBlockBeforeControl
has been removed as the descriptionBlock
is always rendered before the control, and the helpBlock
always after the control.
See the BootstrapUiModule documentation for a full list of release notes.
AdminWebModule
EntityAdminMenu
and EntityAdminMenuEvent
have been deprecated.
These classes were never supposed to be in the AdminWebModule package space, as they belong to EntityModule functionality.
To migrate, simply rename the import package from com.foreach.across.modules.adminweb.menu
to com.foreach.across.modules.entity.views.menu
.
New implementations have been added to EntityModule. Migration is strongly advised, though no code changes are required in this release.
See the AdminWebModule documentation for a full list of release notes.
EntityModule
EntityModule has mainly been updated with new features. See the EntityModule documentation for a full list of release notes.
The following are the most important updates from a migration point of view:
-
the module classpath dependencies have been reworked, allowing EntityModule to be used without either AdminWebModule or BootstrapUiModule (with limited feature availability)
-
as a result those dependencies are no longer pulled in transitively, this might require you to add them manually to existing applications
-
-
EntityModule no longer creates its own
Validator
instance, theregisterForMvc
related settings have been removed. The validator used by EntityModule is always the default MVC validator. -
New implementations of
EntityAdminMenu
andEntityAdminMenuEvent
to replace the equivalent classes from AdminWebModule. You may consider this mostly a package rename for these classes. -
The
descriptionBlock
of aFormGroupElement
for a property is now always rendered before the control, in accordance with the changes in BootstrapUiModule. New message codes have been added to support bothtooltip
andhelpBlock
for a property. The same message codes are also available for aFieldsetFormElement
. -
By default
descriptionBlock
,helpBlock
andtooltip
are only added to aFormGroupElement
orFieldsetFormElement
inViewElementMode.FORM_WRITE
. -
The behaviour for control name prefixing when using an
EntityViewCommand
has changed. Controls are prefixed withentity.
only if there is an additional builder context attribute set totrue
(attribute nameEntityPropertyControlNamePostProcessor.PREFIX_CONTROL_NAMES
). This only done by default in thePropertyRenderingViewProcessor
, used by the default entity views. This change can cause side effects in existing custom forms, developers are encouraged to review those.
Linking to entity views
The new version introduces a new approach for linking to entity views, which might result in breaking changes in some application.
The EntityLinkBuilder
implementation has been deprecated.
A new EntityViewLinks
component has been added as an entry point for building links to entity views.
An EntityConfiguration
contains an EntityViewLinkBuilder
attribute that returns a link builder for that particular entity type.
The older EntityLinkBuilder
attribute is also kept for backwards compatibility, but returns the same instance of the newer implementation.
It is best to review your code and make changes accordingly, even though the new implementation has been kept as backwards compatible as possible.
Within the context of rendering an entity view, you can make the following changes:
String url;
EntityViewContext entityViewContext;
MyEntity entity;
// -- Retrieving the link builder for the entity type
EntityLinkBuilder oldBuilder = entityViewContext.getLinkBuilder(); // OLD
EntityViewLinkBuilder newBuilder = entityViewContext.getLinkBuilder(); // NEW
// -- Linking to the list view
// url: /entities/myEntity
url = oldBuilder.overview(); // OLD
url = newBuilder.listView().toUriString(); // NEW
// -- Linking to the create view
// url: /entities/myEntity/create
url = oldBuilder.create(); // OLD
url = newBuilder.createView().toUriString(); // NEW
// -- Linking to the update view
// url: /entities/myEntity/1/update
url = oldBuilder.update( entity ); // OLD
url = newBuilder.forInstance( entity ).updateView().toUriString(); // NEW
// -- Linking to a custom view
// url: /entities/myEntity/1?view=customViewName
url = UriComponentsBuilder.fromUriString( oldBuilder.view( entity ) )
.queryParam( "view", "customViewName" )
.toUriString(); // OLD
url = newBuilder.forInstance( entity )
.withViewName( "customViewName" )
.toUriString(); // NEW
// -- Linking to an association list view
// url: /entities/myEntity/1/associations/associatedItems/
url = association.getTargetEntityConfiguration()
.getAttribute( EntityLinkBuilder.class )
.asAssociationFor( oldBuilder, entity )
.overview(); // OLD
url = newBuilder.forInstance( entity )
.association( AssociatedItem.class )
.toUriString(); // NEW
// -- Linking to a single associated item update view
// url: /entities/myEntity/1/associations/associatedItems/2/update
url = association.getTargetEntityConfiguration()
.getAttribute( EntityLinkBuilder.class )
.asAssociationFor( oldBuilder, entity )
.update( associatedItem ); // OLD
url = newBuilder.forInstance( entity )
.association( associatedItem )
.updateView()
.toUriString(); // NEW
The new EntityViewLinkBuilder
has a more fluent API that allows customizing the URL before converting it to a String
.
It also has some short-hand methods for commonly used entity view related parameters.
Every method call results in a new instance being created, so you will not make inadvertent changes to an existing link builder.
// append a custom path segment
.slash( String path )
// append query parameter
.withQueryParam( String param, Object... values )
// set a from URL ('from' query parameter)
.withFromUrl( String url )
// set a partial fragment ('_partial' query parameter)
.withPartial( String fragment )
// set a custom view name ('view' query parameter)
.withViewName( String viewName )
// return the unprocessed URI (eg. '@adminWeb:/entities/myEntity')
.toString()
// returning the processed URI (eg. '/admin/entities/myEntity')
.toUriString()
// create a new UriComponentsBuilder with the settings
.toUriComponentsBuilder()
// return as URI
.toUri()
// return as UriComponents
.toUriComponents()
// return the original EntityViewLinks
.root()
A new EntityViewLinks
component is also available to generate links to entity views from anywhere in your code.
@Autowired
EntityViewLinks links;
// link to a list view
links.linkTo( MyEntity.class ).toUriString();
// link to single entity view
links.linkTo( MyEntity.class ).forInstance( myEntity ).toUriString();
links.linkTo( MyEntity.class ).withId( 1 ).toUriString();
// or shorter...
links.linkTo( myEntity ).toUriString();
links.linkTo( myEntity ).updateView().toUriString();
// link to associations
links.linkTo( myEntity ).association( AssociatedEntity.class ).toUriString();
links.linkTo( myEntity ).association( associatedItem ).toUriString();
Contributed Modules
Adding additional spring and auto-configuration compatibility potentially incurs issues with contributed/older modules. In the next sections, known issues will be listed.
ImageServer
The following section lists migration issues with the current version of ImageServer.
Handler mapping
ImageServer specific paths are registered but can no longer be resolved.
This is because an additional path resolver (/**
) has been added through spring compatibility, which accepts any path and has a higher precedence than the ImageServer handler mappings.
This will be fixed in a later release.
To work around the issue, the wildcard static resources path must be removed by setting the property:
# Manually specify a static resource path (remove the default /**)
spring.mvc.static-path-pattern=
Missing database columns
ImageServer requires that the Hibernate default naming strategy is used. You must manually force your application to use the compatible physical naming strategy. Please see the changes to AcrossHibernateModule for all details.