Spring configuration bean with defaults and multiple overrides - java

I am building a Spring Boot application that has reason to call out to multiple HTTP services. I would like to be able to configure my Clients with varying options, e.g. socket timeouts, as well as have a default.
Ideally, I could specify properties like this:
client.default.socket-timeout=500ms
client.slow-service.socket-timeout=1000ms # it's a bit slower
client.fast-service.socket-timeout=100ms
make a simple class like:
class ClientConfiguration {
#Value("${client.<client-name>.socket-timeout:${client.default.socket-timeout:30}}")
int socketTimeout;
}
And then be able to inject:
#Inject
public MyService(#Named("slow-service") ClientConfiguration slowServiceConfig) { ... }
Normally I set my configuration via #Value, but there doesn't appear to be a way to parametrize the key it uses. Especially not based on the bean name. I would love to avoid writing tons of boilerplate to create key names and fetch it from a PropertyResolver or other manual approach.
Does Spring (4.3.0) or Spring Boot (1.4.0.M3) have any good pattern to address this use case?

When we had similar requirement, instead of using individual #Value all over the code, we created a configuration service per group of keys.
#Service
public class SlowClientConfigurationService {
#Value("${client.slow.property.key}")
private String slowValue;
// getters, setters, sanity checks and so on...
}
and inject this service, where ever you need slow client configuration, which can provide multiple key values via getter/setters method, and also it keeps the code clean.
#Service
public class YourService{
#Autowire
private SlowClientConfigurationService configurationService ;
//...
}

Related

Java integration test with fake outbound call

I work on a Java project using Spring framework, JUnit and Mockito.
The application is in the middle of a chain with others application, so it exposes inbound ports (e.g. an HTTP API) to be called and uses outbound ports (e.g. web services and database) to call other apps.
I want to write something like an integration test that should pass through the whole java code from the inbound port to the outbound port, but without doing any call to anything that's outside of the project.
Let's take a very-simple-but-very-concrete example :
We expose an HTTP endpoint to get customers and we call another app to get them.
In the domain : customers are represented by the Customer class.
In the externalapp layer : customers are represented by the CustomerModel class.
In the rest layer : customers are represented by the CustomerDto class.
Thus :
The CustomerSupplierAdapter class gets data from CustomerRepository and does the mapping from CustomerModel to Customer.
The CustomerControllerAdapter class does the mapping from Customer to CustomerDto and returns the data.
Now, I want to test my app by calling the CustomerControllerAdapter's getCustomers(), which will call the real service, which will call the real supplier, which will call a fake repository.
I wrote the following code :
#ExtendWith(SpringExtension.class)
class CustomerIntegrationTest {
#Mock
private CustomerRepository repository;
#InjectMocks
private CustomerControllerAdapter controller;
#BeforeAll
void setupAll() {
CustomerOutboundPort customerOutboundPort = new CustomerSupplierAdapter(repository);
CustomerInboundPort customerInboundPort = new CustomerService(customerOutboundPort);
controller = new CustomerControllerAdapter(customerInboundPort);
}
#Test
void bulkQuery() {
// Given
CustomerModel model = new CustomerModel();
model.setName("Arya Stark");
doReturn(List.of(model)).when(repository).getCustomers();
// When
List<CustomerDto> dtos = controller.getCustomers();
// Then
assertThat(dtos).hasSize(1);
assertThat(dtos.get(0).getName()).isEqualTo("Arya Stark");
}
}
But in this code, I do the "constructor's wiring" by myself in the setupAll() instead of relying on Spring dependency injection. It is not a viable solution because it would be very hard to maintain in real-life context (controller may have multiple services, service may have multiple suppliers, etc).
Actually, I would like to have something like an annotation to set on a CustomerRepository instance to programmatically overload dependency injection. Like : "Hey Spring, if any #Service class needs a CustomerRepository then you should use this fake one instead of the usual concrete implementation" without having to do the wiring by myself.
Is there any way to achieve that using Spring, JUnit, Mockito or anything else ?
If you really want to replace every CustomerRepository in your tests (everywhere!) with a mock, I'd recommend going for a configuration which provides a #Bean, which creates a mocked bean.
#Profile("test")
#Configuration
public class TestConfiguration {
#Bean
#Primary
public CustomerRepository customerRepostiory() {
return Mockito.mock(CustomerRepository.class);
}
}
#MockBean can have negative effects on your test duration as it's quite possible Spring needs to restart it's context.
Alternatively, I'd recommend NOT mocking your repository at all, but instead using either an in memory equivalent (H2) or the TestContainers framework to start the real database for you. Instead of mocking, you insert data into your repository before you start your tests.

Need generic solution for Spring based app for any Data source on Password expire

I don't know, how to approach a solution for the following scenario.
We have a new requirement to remove DB Password from properties even though it's encrypted with Jasypt library or some other algorithms.
Instead of storing the password in properties or LDAP, we need to fetch it dynamically from Cyberark.
Password may expire in a day or two or in a week or in a month. It totally depends on Password expiration policy.
We have multiple projects. Some are web-based and some are standalone. We want to write a generic solution.
How to override getConnection method of any data source like Spring data source, Apache Basic data source (it support extending class), C3P0, DBCP or HikariCP without impacting their behavior and setting the password before hitting super.getConnection()?
super.getConnection(); // Here max attempt will be 3
Spring supports method replacement, but I don't know what will be the impact on the connection pooling framework.
Let me know if you need more details.
To solve your problem you can use spring-cloud-context library and its #RefreshScope annotation. Also, it will be needed for you to develop a bit.
1) You need a special watcher bean which will monitor if the password was changed. It will be smth like this:
#Service
public class Watcher {
private final ContextRefresher refresher;
public Watcher(ContextRefresher refresher) {
this.refresher = refresher;
}
#Scheduled(fixedDelay = 10000L)
public void monitor() {
if (/* smth changed*/) {
refresher.refresh();
}
}
}
So, when you call refresher.refresh(); all beans annotated with #RefreshContext will be disposed and recreated after the first access to them.
2) Annotate your datasource bean with #RefreshContext annotation.
3) You have to provide password to be accessed using #ConfigurationProperties annotation. You will need to create SourceLocator. It will be smth like this
#Order(0)
public class SourceLocator implements PropertySourceLocator {
#Override
public PropertySource<?> locate(Environment environment) {
//Load properties to hash map
return new MapPropertySource("props", new HashMap<>());
}
}
Also, create a file spring.factories and put the following data there:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.test.YourSourceLocator
4) Create properties class where your db pass will be held and refreshed.
#RefreshScope
#ConfigurationProperties(prefix="your.prefix")
public class Properties {
private String dbPassword;
}
Autowire this bean to the configuration where you create your datasource and use password from it.

Is it possible to make Spring #Import or #Configuration parametrized?

I've created a lot of common small bean-definition containers (#Configuration) which I use to rapidly develop applications with Spring Boot like:
#Import({
FreemarkerViewResolver.class, // registers freemarker that auto appends <#escape etc.
ConfigurationFromPropertiesFile.class, // loads conf/configuration.properties
UtfContentTypeResponse.class, // sets proper Content-language and Content-type
LocaleResolverWithLanguageSwitchController // Locale resolver + switch controller
);
class MySpringBootApp ...
For example, one of such #Configurations can set up session storage for locale cookie with web controller to switch to selected language etc.
They are very fun to work with and reuse, but it would be really great to make it parametrized, which could allow lot more reusege. I mean something like:
Pseudo code:
#Imports( imports = {
#FreemarkerViewResolver( escapeHtml = true, autoIncludeSpringMacros = true),
#ConfigurationFromProperties( path = "conf/configuration.properties" ),
#ContentTypeResponse( encoding = "UTF-8" ),
#LocaleResolver( switchLocaleUrl = "/locale/{loc}", defaultLocale = "en"
})
So, I basically mean "configurable #Configurations". What would be the best way to make the configuration that way?
Maybe something more like this (again, pseudo code):
#Configuration
public class MyAppConfiguration {
#Configuration
public FreemarkerConfiguration freemarkerConfiguration() {
return FreemarkerConfigurationBuilder.withEscpeAutoAppend();
}
#Configuration
public ConfigurationFromPropertiesFile conf() {
return ConfigurationFromPropertiesFile.fromPath("...");
}
#Configuration
public LocaleResolverConfigurator loc() {
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale("en").withSwitchUrl("/switchlocale/{loc}");
}
Let me quote Spring Boot Reference Guide - Externalized Configuration:
"Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments."
In my opinion the customization is not done at import time via annotation parameters like in your 2nd pseudo code block, instead the customization happens at run time e.g. in the configuration classes. Let me adapt your 3rd code block (only one function):
#Configuration
public class MyAppConfiguration {
#Autowired
private Environment env;
// Provide a default implementation for FreeMarkerConfigurer only
// if the user of our config doesn't define her own configurer.
#Bean
#ConditionalOnMissingBean(FreeMarkerConfigurer.class)
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
result.setTemplateLoaderPath("/WEB-INF/views/");
return result;
}
...
#Bean
public LocaleResolverConfigurator loc() {
String defaultLocale = env.getProperty("my.app.config.defaultlocale", "en");
String switchLocale = env.getProperty("my.app.config.switchlocale", "/switchlocale/{loc}");
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale(defaultLocale).withSwitchUrl(switchLocale);
}
For LocaleResolverConfigurator the configuration is read from the environment, meaningful default values are defined. It is easy to change the default value(s) by providing a different value for a config parameter in any of the supported ways (documented in the first link) - via command line or a yaml file. The advantage over annotation parameters is that you can change the behavior at run time instead of compile time.
You could also inject the config parameters (if you prefer to have them as instance variable) or use a lot of other conditions, e.g. #ConditionalOnMissingBean, #ConditionalOnClass, #ConditionalOnExpression and so on. For example with #ConditionalOnClass you could check if a particular class is on your class path and provide a setting for the library identified by this class. With #ConditionalOnMissingClass you could provide an alternative implementation. In the example above I used ConditionalOnMissingBean to provide a default implementation for the FreeMarkerConfigurer. This implementation is only used when no FreeMarkerConfigurer bean is available thus can be overridden easily.
Take a look at the starters provided by Spring Boot or the community. A good read is also this blog entry. I learned a lot from spring-boot-starter-batch-web, they had an article series in a German Java magazine, but parts are also online, see Boot your own infrastructure – Extending Spring Boot in five steps (MUST READ) and especially the paragraph "Make your starter configurable by using properties".
Though I like the idea of having imports be parameterized, I think that as it stands now using #Import and #Configuration not a good fit.
I can think of two ways to use dynamic configurations, that don't rely on PropertySource style configuration.
Create a custom #ImportConfig annotation and annotation processor that accepts configuration properties that are hard-coded into the generated source files.
Use a BeanFactoryPostProcessor or BeanPostProcessor to add or manipulate your included beans respectively.
Neither is particularly simple IMO, but since it looks like you have a particular way of working. So it could be worth the time invested.

Create Spring #Service instance with #Transactional methods manually from Java

Let's say there are #Service and #Repository interfaces like the following:
#Repository
public interface OrderDao extends JpaRepository<Order, Integer> {
}
public interface OrderService {
void saveOrder(Order order);
}
#Service
public class OrderServiceImpl implements OrderService {
#Autowired
private OrderDao orderDao;
#Override
#Transactional
public void saveOrder(Order order) {
orderDao.save(order);
}
}
This is part of working application, everything is configured to access single database and everything works fine.
Now, I would like to have possibility to create stand-alone working instance of OrderService with auto-wired OrderDao using pure Java with jdbcUrl specified in Java code, something like this:
final int tenantId = 3578;
final String jdbcUrl = "jdbc:mysql://localhost:3306/database_" + tenantId;
OrderService orderService = someMethodWithSpringMagic(appContext, jdbcUrl);
As you can see I would like to introduce multi-tenant architecture with tenant per database strategy to existing Spring-based application.
Please note that I was able to achieve that quite easily before with self-implemented jdbcTemplate-like logic also with JDBC transactions correctly working so this is very valid task.
Please also note that I need quite simple transaction logic to start transaction, do several requests in service method in scope of that transaction and then commit it/rollback on exception.
Most solutions on the web regarding multi-tenancy with Spring propose specifying concrete persistence units in xml config AND/OR using annotation-based configuration which is highly inflexible because in order to add new database url whole application should be stopped, xml config/annotation code should be changed and application started.
So, basically I'm looking for a piece of code which is able to create #Service just like Spring creates it internally after properties are read from XML configs / annotations. I'm also looking into using ProxyBeanFactory for that, because Spring uses AOP to create service instances (so I guess simple good-old re-usable OOP is not the way to go here).
Is Spring flexible enough to allow this relatively simple case of code reuse?
Any hints will be greatly appreciated and if I find complete answer to this question I'll post it here for future generations :)
HIbernate has out of the box support for multi tenancy, check that out before trying your own. Hibernate requires a MultiTenantConnectionProvider and CurrentTenantIdentifierResolver for which there are default implementations out of the box but you can always write your own implementation. If it is only a schema change it is actually pretty simple to implement (execute a query before returning the connection). Else hold a map of datasources and get an instance from that, or create a new instance.
About 8 years ago we already wrote a generic solution which was documented here and the code is here. It isn't specific for hibernate and could be used with basically anything you need to switch around. We used it for DataSources and also some web related things (theming amongst others).
Creating a transactional proxy for an annotated service is not a difficult task but I'm not sure that you really need it. To choose a database for a tenantId I guess that you only need to concentrate in DataSource interface.
For example, with a simple driver managed datasource:
public class MultitenancyDriverManagerDataSource extends DriverManagerDataSource {
#Override
protected Connection getConnectionFromDriverManager(String url,
Properties props) throws SQLException {
Integer tenant = MultitenancyContext.getTenantId();
if (tenant != null)
url += "_" + tenant;
return super.getConnectionFromDriverManager(url, props);
}
}
public class MultitenancyContext {
private static ThreadLocal<Integer> tenant = new ThreadLocal<Integer>();
public static Integer getTenantId() {
return tenant.get();
}
public static void setTenatId(Integer value) {
tenant.set(value);
}
}
Of course, If you want to use a connection pool, you need to elaborate it a bit, for example using a connection pool per tenant.

Spring #Value annotation always evaluating as null?

So, I have a simple properties file with the following entries:
my.value=123
another.value=hello world
This properties file is being loaded using a PropertyPlaceHolderConfigurer, which references the properties file above.
I have the following class, for which I'm trying to load these properties in to like so:
public class Config
{
#Value("${my.value}")
private String mValue;
#Value("${another.value}")
private String mAnotherValue;
// More below...
}
The problem is that, mValue and mAnotherValue are ALWAYS null... yet in my Controllers, the value is being loaded just fine. What gives?
If instances of Config are being instantiated manually via new, then Spring isn't getting involved, and so the annotations will be ignored.
If you can't change your code to make Spring instantiate the bean (maybe using a prototype-scoped bean), then the other option is to use Spring's load-time classloader weaving functionality (see docs). This is some low-level AOP which allows you to instantiate objects as you normally would, but Spring will pass them through the application context to get them wired up, configured, initialized, etc.
It doesn't work on all platforms, though, so read the above documentation link to see if it'll work for you.
I had similar issues but was a newbie to Spring.
I was trying to load properties into an #Service, and tried to use #Value to retrieve the property value with...
#Autowired
public #Value("#{myProperties['myValue']}") String myValue;
I spend a whole day trying various combinations of annotations, but it always returned null.
In the end the answer as always is obvious after the fact.
1) make sure Spring is scanning your class for annotations by including the package hierachy
In your servlet.xml (it will scan everything below the base value you insert.
2) Make sure you are NOT 'new'ing the class that you just told Spring to look at. Instead, you use #Autowire in the #Controller class.
Everything in Spring is a Singleton, and what was happening was Spring loaded the values into its Singleton, then I had 'new'ed another instance of the class which did not contain the newly loaded values so it was always null.
Instead in the #Controller use...
#Autowired
private MyService service;
Debugging...
One thing I did to find this was to extend my Service as follows...
#Service
public class MyService implements InitializingBean
Then put in debug statements in...
#Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
LOGGER.debug("property myValue:" + myValue);
}
Here I could see the value being set on initialization, and later when I printed it in a method it was null, so this was a good clue for me that it was not the same instance.
Another clue to this error was that Tomcat complained of Timeouts trying to read from the Socket with Unable to parse HTTPheader... This was because Spring had created an instance of the service and so had I, so my one was doing the real work, and Spring was timing out on its instance.
See my answer here.
I ran into the same symptoms (#Value-annotated fields being null) but with a different underlying issue:
import com.google.api.client.util.Value;
Ensure that you are importing the correct #Value annotation class! Especially with the convenience of IDEs nowadays, this is a VERY easy mistake to make (I am using IntelliJ, and if you auto-import too quickly without reading WHAT you are auto-importing, you might waste a few hours like I did).
The correct import is:
org.springframework.beans.factory.annotation.Value
As its working with #Controller, it seems you are instantiating Config yourself. Let the Spring instantiate it.
You can also make your properties private, make sure your class is a Spring bean using #Service or #Component annotations so it always gets instantiated and finally add setter methods annotated with #Value . This ensures your properties will be assigned the values specified in your application.properties or yml config files.
#Service
public class Config {
private static String myProperty;
private static String myOtherProperty;
#Value("${my.value}")
public void setMyProperty(String myValue) {
this.myProperty = myValue;}
#Value("${other.value}")
public void setMyOtherProperty(String otherValue) {
this.myOtherProperty = otherValue;}
//rest of your code...
}
Add <context:spring-configured /> to you application context file.
Then add the #Configurable annotation to Config class.
In my case in my unit test, executeScenarioRequest always is null
#RunWith(SpringRunner.class)
#ExtendWith(MockitoExtension.class)
class ScenarioServiceTestOld {
#Value("classpath:scenario/SampleScenario.json")
Resource executeScenarioRequest;
Change #ExtendWith(MockitoExtension.class) to #ExtendWith(SpringExtension.class)

Categories

Resources