Configure hierarchical Spring application contexts individually - spring

We have multiple application contexts in our application and use them in a hierarchical order:
new SpringApplicationBuilder( HierarchicalApplication.class )
.logStartupInfo( false )
.bannerMode( Mode.OFF )
.child( ChildContext1.class )
.logStartupInfo( false )
.bannerMode( Mode.OFF )
.sibling( ChildContext2.class )
.logStartupInfo( false )
.bannerMode( Mode.OFF )
.run( args );
Each of the child contexts has an own application.properties file, which is used to configure the corresponding context. However, we need to configure them individually via system properties. For instance: Both ChildContext1 and ChildContext2 would have own datasources with different usernames. Usually one could just use spring.datasource.username, but setting this as system property would override the property for both contexts. So instead we need properties like child1.spring.datasource.username and child2.spring.datasource.username. Obvsiously we could just duplicate them explicitly in the child's properties files:
child1.spring.datasource.username=postgres
spring.datasource.username=${child1.spring.datasource.username}
However, this would mean we would need to write a lot of boilerplate properties.
Our idea was to provide a certain property (modulename or prefix) in both contexts which would define a fixed prefix for the properties. We though then about using some kind of EnvironmentPostProcessor, but the post processor cannot see this property modulename. ApplicationListeners doesn't seem to be called early enough and we are not sure if we can solve our issue with a PropertySource.
Any ideas?

We found a solution with minor drawbacks.
First we have to add the children's module names in the root application:
new SpringApplicationBuilder( HierarchicalApplication.class )
.logStartupInfo( false )
.bannerMode( Mode.OFF )
.child( ChildContext1.class )
.logStartupInfo( false )
.properties( "modulename=Child1" )
.bannerMode( Mode.OFF )
.sibling( ChildContext2.class )
.logStartupInfo( false )
.properties( "modulename=Child2" )
.bannerMode( Mode.OFF )
.run( args );
Now we add a EnvironmentPostProcessor (registered via a spring.factories file) which searches for all system properties with the modulename as prefix, removes the prefixes and adds those modified properties as a new property source.
#Override
public void postProcessEnvironment( final ConfigurableEnvironment environment, final SpringApplication application ) {
final String modulename = environment.getProperty( "modulename" );
final Map<String, Object> environmentWithoutPrefix = environment.getSystemProperties( )
.entrySet( )
.stream( )
.filter( entry -> entry.getKey( ).startsWith( modulename ) )
.collect( Collectors.toMap( entry -> removePrefix( modulename, entry.getKey( ) ), entry -> entry.getValue( ) ) );
environmentWithoutPrefix.put( "modulename", modulename );
environment.getPropertySources( ).addFirst( new MapPropertySource( "modifiedEnvironment", environmentWithoutPrefix ) );
}
The drawback is, that we only consider system properties here (other properties are not necessarily loaded yet). This is acceptable for us, at it was always our use case to configure the modules via environment variables.

Related

Laravel Mix - Is Chaining Required to Ensure Execution Order?

TLDR;
Do you have to chain Laravel Mix methods to maintain the execution order? Are any methods async that would prevent one from using the following non-chaining pattern, mix.scripts(); mix.js(); mix.sass();?
The few tests I've run suggest I do not need to chain.
An Example
Due to how our Laravel app is setup, we need to have more that one Laravel Mix setup. Instead of copy-n-pasting a webpack.mix.js file and modifying a few lines here and there in each file, we're looking at creating a config object that is passed to a singular webpack.mix.js file. In this file, we would check if various things have been configured, and if so, run the appropriate Mix method. Below is a pseudo-code example.
if ( config.js ) {
mix.js( config.js.src, config.js.dist );
}
if ( config.sass ) {
mix.sass( config.sass.src, config.sass.dist );
}
if ( config.concat ) {
if ( config.concat.styles ) {
// Could be more than one set of files that need to be combined, so array.
config.concat.styles.map( ( files ) => {
mix.styles( files.src, files.dist );
}
}
if ( config.concat.scripts ) {
// Could be more than one set of files that need to be combined, so array.
config.concat.scripts.map( ( files ) => {
mix.scripts( files.src, files.dist );
}
}
}
Currently, our code is more like most examples you see on the web.
mix
.options()
.webpackConfig()
.styles()
.styles()
.scripts()
.js()
.sass();
laravel-mix abstracts configuration of webpack and dynamically generates the webpack config.
The organization of its API implementation is done using Builder pattern with a fluent or chainable interface.
This makes it such that to produce a particular configuration only steps that are necessary to be performed have to be called.
You need to ensure that code in your webpack.mix.js module can be properly imported.
You need to be careful about the ordering of custom tasks such as copy, copyDirectory, combine, version. In v5.0.0, custom tasks are run without any bearing on their asynchronous nature. However there is coming changes to see to it that they are run sequentially.
Other API methods can be called in any order.
The few tests I've run suggest I do not need to chain.
You're absolutly correct!
Laravel Mix is written in JavaScript and makes use of Method Chaining.
You can visualize code execution with OnlinePythonTutor.
If you look at the code below, you'll find that you don't necessarily need to chain methods to maintain execution order.
class Person {
setName(name) {
this.name = name
return this
}
setAge(age) {
this.age = age
return this
}
}
var p = new Person()
p.setName("Alice").setAge(42) // Set name before age
p.setName("Bob") // Set name
p.setAge(42) // Set age
You can visualize this code here

Customize DateTimeField in Vaadin 8

In DateTimeField component, it displays both date (year, month, day) and hour (hour, minute, second).
Now i don't want to get date. Any idea to only allow to show and get time ( hour:minute:second) ?
tl;dr
No built-in way to display time-of-day only, as of Vaadin 8.1.
You can make your own.
Details
The existing DateTimeField]() widget supports only the legacy GregorianCalendar class which is a combination of a date and a time-of-day plus a time zone. So not useful for time-of-day only values.
Unfortunately, as of Vaadin 8.1, it seems the bundled field widgets have not yet been updated for the java.time types such as the java.time.LocalTime you would want.
As a workaround for the lack of an intelligent LocalTime-savvy field, I suggest either:
Try the date-time-fields Add-on.An add-on is an extension you can add to your Vaadin project, to deliver some nugget of Vaadin-related functionality. An add-on may be visual widget related, or may be non-visual backend feature. If visual such as this, you may need to do a rebuild of your project to cause the widgetset to be re-created. Perhaps a Maven clean and install (not sure as the technique for this keeps changing with various Vaadin releases).
Make your own. Use a regular text field. Write a validator to verify user input parses as a LocalTime object. You have a choice of writing a Vaadin-specific validator or a standard Bean Validation validator. See How to add Validators in Vaadin 8?
Example
Here is some rough code for creating your own LocalTime-savvy field. This is by no means complete, but may help point you in the right direction.
final Label whenLabel = new Label( "when?" );
final TextField whenField = new TextField( );
whenField.setCaption( "When:" );
whenField.setValueChangeMode( ValueChangeMode.BLUR );
whenField.addValueChangeListener( new HasValue.ValueChangeListener < String >( )
{
static final long serialVersionUID = 201710132100L;
#Override
public void valueChange ( HasValue.ValueChangeEvent < String > valueChangeEvent )
{
String input = whenField.getValue( );
System.out.println( "input: " + input );
try
{
// DateTimeFormatter f = DateTimeFormatter.ISO_LOCAL_TIME; // Constant ISO_LOCAL_TIME is for time-of-day in standard ISO 8601 format.
// DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime( FormatStyle.SHORT ).withLocale( Locale.CANADA_FRENCH ); // Automatically localize.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime( FormatStyle.SHORT ).withLocale( Locale.US ); // Automatically localize.
LocalTime localTime = LocalTime.parse( input , f );
String timeIso8601 = localTime.toString( );
whenLabel.setValue( timeIso8601 );
} catch ( DateTimeParseException e )
{
whenLabel.setValue( e.getClass().getCanonicalName() );
System.out.println( "ERROR - failed to parse input: " + input );
}
}
} );
In my solution I used a TextField and added a custom validator to the relevant binder:
.withValidator(this::validateTime, "Invalid time")
I implemented the validate method as follows:
private boolean validateTime(final String timeString) {
try {
LocalTime.parse(timeString);
return true;
} catch (final DateTimeParseException e) {
return false;
}
}
now Vaadin 14.2.x provided new DateTimePicker component to solve this problem.
https://vaadin.com/releases/vaadin-14

ConfigurationProperties loading list from YML

I'm trying to load Configuration from YML. I can load value and I can also load list if these are comma seperated values. But i can't load a typical YML List.
Configuration Class
#Component
#PropertySource("classpath:routing.yml")
#ConfigurationProperties
class RoutingProperties(){
var angular = listOf("nothing")
var value: String = ""
}
Working routing.yml
angular: /init, /home
value: Hello World
Not Working routing.yml
angular:
- init
- home
value: Hello World
Why can't i load the second version of yml / do I have a syntaxt error?
ENV: Kotlin, Spring 2.0.0.M3
As #flyx say, #PropetySource not worked with yaml files. But in spring you may override almost everything :)
PropertySource has additional parameter: factory. It's possible to create your own PropertySourceFactory base on DefaultPropertySourceFactory
open class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
override fun createPropertySource(name: String?, resource: EncodedResource?): org.springframework.core.env.PropertySource<*> {
if (resource == null)
return super.createPropertySource(name, resource)
return YamlPropertySourceLoader().load(resource.resource.filename, resource.resource, null)
}
}
And when use this factory in propertysource annotation:
#PropertySource("classpath:/routing.yml", factory = YamlPropertyLoaderFactory::class)
Last that you need is to initialized variable angular with mutableList
Full code sample:
#Component
#PropertySource("classpath:/routing.yml", factory = YamlPropertyLoaderFactory::class)
#ConfigurationProperties
open class RoutingProperties {
var angular = mutableListOf("nothing")
var value: String = ""
override fun toString(): String {
return "RoutingProperties(angular=$angular, value='$value')"
}
}
open class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
override fun createPropertySource(name: String?, resource: EncodedResource?): org.springframework.core.env.PropertySource<*> {
if (resource == null)
return super.createPropertySource(name, resource)
return YamlPropertySourceLoader().load(resource.resource.filename, resource.resource, null)
}
}
#SpringBootApplication
#EnableAutoConfiguration(exclude = arrayOf(DataSourceAutoConfiguration::class))
open class Application {
companion object {
#JvmStatic
fun main(args: Array<String>) {
val context = SpringApplication.run(Application::class.java, *args)
val bean = context.getBean(RoutingProperties::class.java)
println(bean)
}
}
}
Kinda old post, i know. But i am at the very same topic right now.
As of now, it seems that PropertySource does indeed work with yaml Files. Given the restriction that it only allows for primitive types (it seems) and it cant handle nested elements. I'm probably gonna dig a bit deeper and update my answer accordingly, but as of now, the accepted answer seems like a functioning workaround.
Well, according to the docs, your YAML file will be rewritten into a property file. The first YAML file becomes:
angular=/init, /home
value=Hello World
While the second one becomes:
angular[0]=init
angular[1]=home
value=Hello World
These are obviously two very different things and therefore behave differently.
Moreover, later in the docs, it is stated that YAML does not even work with #PropertySource:
24.6.4 YAML shortcomings
YAML files can’t be loaded via the #PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.
That makes me kind of wonder why the first case works for you at all.
The docs say this about the generated …[index] properties:
To bind to properties like that using the Spring DataBinder utilities (which is what #ConfigurationProperties does) you need to have a property in the target bean of type java.util.List (or Set) and you either need to provide a setter, or initialize it with a mutable value, e.g. this will bind to the properties above
So, let's have a look at Kotlin docs: listOf returns a new read-only list of given elements. So the list is not mutable as required by the docs, which I assume is why it doesn't work. Try using a mutable list (since I have never used Kotlin, I cannot give you working code). Also try to declare it as java.util.List if that's possible in Kotlin.

Libtorrent 1.1.0 issues

I'm using this method here to have the session available globally but the latest updates changed how things are working.
using boost::shared_ptr;
using boost::weak_ptr;
using libtorrent::aux::session_impl;
using namespace libtorrent;
libtorrent::session* session;
libtorrent::settings_pack* pack;
bool start ( )
{
pack->set_str ( user_agent, std::string ( "Test " ) + TEST_VERSION_STRING );
/////
return true
}
I'm getting user_agent is undefined and after a search of the source I cant see it defined anywhere, libtorrent/settings_pack.hpp is included.

Additional parameters of GUILayout.Label

The Unity manual does not give examples of the params in the following particular version of GUILayout.Label (or is it somewhere else that I cannot seem to find?)
public static void Label(Texture image, GUIStyle style, params GUILayoutOption[] options);
So, I am wondering how to change the font size of the following code that I am dealing with:
I have a normal OnGUI() in an Editor file:
GUILayout.BeginHorizontal( displayStyle1 );
GUILayout.Label( "Has title?" );
if ( hasTitle )
{
if( GUILayout.Button( "yes", GUILayout.Width(40) ) )
{
hasTitle = true;
}
GUILayout.EndHorizontal();
}
and I have my own MyOnGUI() in an Executor file:
if( fieldInput.HasTitle )
{
GUILayout.BeginHorizontal( displayStyle1 );
GUILayout.Label( fieldInput.Title, displayStyle1 );
GUILayout.EndHorizontal();
}
Once you press yes and enter the title in the Editor, what you get after the Executor runs needs to be in bigger font, so I thought I should modify this line:
GUILayout.Label( fieldInput.Title, displayStyle1 );
therefore, I need to see an example of how to specify a bigger font as 3rd parameter...
Is this possible? Is it the right way of directly changing font size without modifying the set styles?
var style= GUI.skin.GetStyle("label");
style.fontSize = 24; // whatever you set
GUILayout.Label( fieldInput.Title, style);

Resources