I have this Configuration class:-
#Configuration
public class AppConfig {
#Autowired
private Environment env;
private List<Weather> weathers = new ArrayList<>();
#Bean
public RestTemplate restTemplate() {
var factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(3000);
factory.setReadTimeout(3000);
return new RestTemplate(factory);
}
#PostConstruct
public void startMonitoring() {
String url = env.getProperty("openWeatherUrl");
String apiKey = env.getProperty("openWeatherApiKey");
try (InputStream resource = app.class.getResourceAsStream("/config.csv")) {
CSVReader reader = new CSVReader(new InputStreamReader(resource));
String[] lineInArray;
while ((lineInArray = reader.readNext()) != null) {
Weather weather = new Weather();
if (!lineInArray[0].contains("city")) {
weather.setCity(lineInArray[0]);
} else {
continue;
}
if (!lineInArray[1].contains("temperaturesLimit")) {
weather.setTemperaturesLimit(Float.parseFloat(lineInArray[2]));
}
if (!lineInArray[2].contains("temperaturesLimit")) {
weather.setFrequency(Integer.parseInt(lineInArray[2]));
}
URI uri = new URI(url + "?q=" + weather.getCity() + "&units=metric&APPID=" + apiKey);
weather.setUri(uri);
weathers.add(weather);
}
} catch (IOException | CsvValidationException | URISyntaxException e) {
System.out.println(e.getMessage());
}
fetchWeatherData();
}
void fetchWeatherData(){
RestTemplate restTemplate = restTemplate();
weathers.forEach(weather -> {
var res = restTemplate.getForEntity(weather.getUri(), Weather.class);
});
}
}
I got error:-
The dependencies of some of the beans in the application context form a cycle:
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
Why is it circular? How do I fix it?
It's quite simple #PostConstruct in AppConfig relies on the RestTemplate bean but this #Bean is part of AppConfig.
This is due to how #Configuration classes work, they are proxied and a call to public RestTemplate restTemplate() would call the proxied method.
A simple fix would be removing the #Bean annotation from your RestTemplate method, if the RestTemplate is not used in other locations.
Related
I am trying to test the AWS SecretManager call using Mockito but when I run the program, I am getting the Null Pointer Exception.
#ExtendWith(MockitoExtension.class)
class XXXX{
String secret = "{ \"client_id\": \"XXXXXX\",\"client_secret\": \"XXXXXX\"} ";
#Mock
AWSSecretsManager secretsClient;
#Mock
GetSecretValueRequest secretValueRequest;
#Mock
GetSecretValueResult secretValueResult;
#BeforeEach
public void setUp(){
lenient().when(secretsClient.getSecretValue(secretValueRequest)).thenReturn(secretValueResult);
lenient().when(secretValueResult.getSecretString()).thenReturn(secret);
}
}
Here, when I am running, I am getting the NullPointerException at when(secretsClient.getSecretValue(secretValueRequest)). It says as secretsClient.getSecretValue(secretValueRequest) is null which is passed as parameter to when(). Any suggestion or advice what I am doing wrong here, please.
You need to install the mockito extension via: #ExtendWith(MockitoExtension.class)
(I think you'll need to make the member variables non-private too).
More clues here: https://www.baeldung.com/mockito-junit-5-extension
Have you tried setting the value on the GetSecretVaueResult first then returning it; something like this?
#Mock
AWSSecretsManager secretsClient;
GetSecretValueResult secretValueResult = new GetSecretValueResult();
secretValueResult.setSecretString("{\"client_id\": \"XXXXXX\",\"client_secret\": \"XXXXXX\"}");
when(secretsClient.getSecretValue(any(GetSecretValueRequest.class))).thenReturn(secretValueResult);
I would determine how you are building your AWSSecretsManager instance within your getSecret() method.
Consider if you are using a getSecret() method similar to the one AWS provides like the following:
public static void getSecret() {
String secretName = "arn:aws:secretsmanager:us-east-1:xxxxxxx";
String region = "us-east-1";
// Create a Secrets Manager client
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.build();
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
.withSecretId(secretName);
GetSecretValueResult getSecretValueResult;
try {
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
} catch (Exception e) {
logger.error("Error retrieving secret: {0}", e);
throw e;
}
...
}
In this case, mocking AWSSecretsManager within your JUnit test will not have the desired outcome because the getSecret() method is instantiating AWSSecretsManagerClientBuilder and assigning it to client each time getSecret() is called. Instead, you can add a configuration class with an AWSSecretsManager bean as and then autowire it in the constructor of the class that contains the getSecret() method.
Add Configuration
#Configuration
public class Config {
#Value("${cloud.aws.region.static}")
private String region;
#Bean
public AWSSecretsManager awsSecretsManager(String region) {
return AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.build();
}
}
Update getSecret()
After doing so, your method should look more like this
private String getSecret() {
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
.withSecretId(secretName);
GetSecretValueResult getSecretValueResult;
try {
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
} catch (Exception e) {
logger.error("Error retrieving secret: {0}", e);
throw e;
}
...
}
Test
Now, you will be able to mock the AWSSecretsManager as intended:
#Mock
AWSSecretsManager client;
private final YourClass undertest;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
undertest = new YourClass(...)
}
#Test
void testYourClass() {
GetSecretValueResult expected = new GetSecretValueResult();
expected.setSecretString("{\"client_id\": \"XXXXXX\",\"client_secret\": \"XXXXXX\"}");
when(client.getSecretValue(any(GetSecretValueRequest.class)))
.thenReturn(expected);
...
}
solution is create real GetSecretValueResponse:
GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(secretValue).build();
so my test is:
public class AWSSecretsManagerTest {
#InjectMock SecretsManagerClient client;
#Inject AWSSecretsManager secretsManager;
#Test
void getSecret_GetSecretStringByName() {
// FIXTURE
var secretValue = "some-value";
GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(secretValue).build();
when(client.getSecretValue((GetSecretValueRequest) any())).thenReturn(response);
// exercise
var result = secretsManager.getSecret("some-secret");
//verify
Assertions.assertEquals(secretValue, result);
}
}
my manager:
#ApplicationScoped
public class AWSSecretsManager implements SecretsManager {
public static final String VERSION_STAGE = "AWSCURRENT";
#Inject
SecretsManagerClient secretsManagerClient;
private GetSecretValueRequest generateGetSecretValueRequest(String secretName) {
return GetSecretValueRequest.builder()
.secretId(secretName)
.versionStage(VERSION_STAGE)
.build();
}
public String getSecret(String secretName) {
return secretsManagerClient.getSecretValue(generateGetSecretValueRequest(secretName)).secretString();
}
}
I inherited a Spring Boot 1.5 project which cannot be migrated up at the moment and need to work on session registry to manage users (ex: list logged users, email users of production updates etc.)
I tried all existing SO solutions I could find, but all of them provide a null result for sessionRegistry().getAllPrincipals(). My security config looks like:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic().disable()
.formLogin().disable()
.headers().frameOptions().sameOrigin()
.and()
.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry());
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher());
}
}
My Application config looks like:
#EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600000)
#EnableDiscoveryClient
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
#EnableZuulProxy
#EnableFeignClients
#EnableSwagger2
#SpringBootApplication
#RibbonClients({
#RibbonClient(name = "employeeService", configuration = StickySessionEditorRibbonConfiguration.class)
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public PreFilter preFilter() {
return new PreFilter();
}
#Bean
public PostFilter postFilter() {
return new PostFilter();
}
#Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
serializer.setCookieMaxAge(3600000);
return serializer;
}
}
Relevant code to access the session registry looks like this:
public class TestController extends BaseController {
#Autowired
private SessionRegistry sessionRegistry;
...
public List<User> findAllLoggedInUsers() {
final List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
}
}
Using the actuator/bean endpoint I can see that the SessionRegistry Bean is active.
I am logging in successfully from a couple of browsers but allPrincipals is always size 0 before and after the logins.
Am at a loss why, and any help here is much appreciated.
Based on #M.Deinum 's comment regarding disabled login I want to add that the project uses Zuul Filters (preFilter and PostFilter) as indicated in the application config. We have an account-manager service completely different from this api-gateway service, which authenticates users based on simple login/password.
The logic in preFilter looks like this:
public class PreFilter extends BaseFilter {
#Autowired
SessionRegistry sessionRegistry;
#Autowired
private SessionRepository sessionRepository;
#Override
public String filterType() {
return "pre";
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest req = ctx.getRequest();
HttpSession session = req.getSession();
try {
String uri = req.getRequestURI();
// user assumed is not authenticated
String authToken = null;
//Login code
if (uri.contains("/api/public/authorization/login") && req.getMethod().equals("POST")) {
session.removeAttribute(AUTH_TOKEN_HEADER);
LoginRequest request = createLoginRequest(req);
/* LoginRequest basically contains "userName" and "password" entered by user */
ResponseEntity<MessageWrapper<String>> response = accountManagerFeignClient.authenticate(loginRequest);
authToken = response.getBody().getData();
if (authToken != null) {
session.setAttribute(AUTH_TOKEN_HEADER, authToken);
ctx.setResponseStatusCode(HttpStatus.OK.value());
ctx.setSendZuulResponse(false);
return null;
}
// authToken == null implies the user was not authenticated by accountManager
} else if ("internal or public apis are called, they won't need authentication") {
// user remains unauthenticated, which is fine for public or internal apis
return null;
} else {
// Assume this is a protected API and provide authToken if one exists
authToken = (String) session.getAttribute(AUTH_TOKEN_HEADER);
}
if (authToken == null)
throw new Exception(UNAUTHORIZED + ". Log String: " + logString);
// Validated user will go through here
ctx.addZuulRequestHeader(AUTH_TOKEN_HEADER, authToken);
} catch (Exception ex) {
ctx.setResponseBody(UNAUTHORIZED);
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
ctx.setSendZuulResponse(false);
}
return null;
}
}
The only relevant logic in postFilter (similar to preFilter) disables sessions during logout in this manner:
if (authToken != null) {
session.removeAttribute(AUTH_TOKEN_HEADER);
session.removeAttribute(StickySessionEditorRule.STICKY_ID);
session.removeAttribute(StickySessionWSGRule.STICKY_ID);
ctx.setResponseBody(LOGGED_OUT);
ctx.setResponseStatusCode(HttpStatus.OK.value());
ctx.setSendZuulResponse(false);
}
session.invalidate();
My other time-consuming option would be to use the HTTPSessionBindingListener as shown here . I have not tried that yet.
Finally, if none of the above work, how could I work directly with redis and do a findAll() ? It looks like there is a SessionRepository, but I could not find a documented way of using it.
Thank You.
I have what I think is a pretty standard StorageService that exists in a Spring Boot app for a web API.
#Component
#Service
#Slf4j
public class StorageService {
#Autowired
private AmazonS3 s3Client;
#Autowired
private RestTemplate restTemplate;
#Value("${app.aws.s3.bucket}")
private String bucket;
#Async
public boolean fetchAndUpload(List<URI> uris) {
List<CompletableFuture<PutObjectResult>> futures = uris.stream().map(uri ->
fetchAsync(uri).thenApplyAsync((asset) -> put(getName(uri.toString()), asset))
).collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();
return true;
}
#Async
private CompletableFuture<InputStream> fetchAsync(URI uri) {
return CompletableFuture.supplyAsync(() -> {
InputStream resp;
try {
// asdf is null here when running my unit tests
Resource asdf = restTemplate.getForObject(uri, Resource.class);
resp = Objects.requireNonNull(asdf).getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
return resp;
});
}
private PutObjectResult put(String name, InputStream data) {
PutObjectRequest request = new PutObjectRequest(bucket, name, data, new ObjectMetadata());
return s3Client.putObject(request);
}
}
Here is an integration test, which at minimum successfully fetches the images given by the integration test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureWebClient
public class StorageServiceIT {
#Value("${app.aws.s3.access.key}")
private String accessKey;
#Value("${app.aws.s3.secret.key")
private String secretKey;
#Spy
private AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.withRegion(Regions.US_EAST_1)
.build();
#Spy
private RestTemplate restTemplate = new RestTemplateBuilder().build();
#MockBean
private SignService signingService;
#Autowired
private StorageService storageService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void fetchAsync() throws URISyntaxException {
List<URI> uris = List.of(
new URI("https://upload.wikimedia.org/wikipedia/commons/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg"),
new URI("https://upload.wikimedia.org/wikipedia/commons/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg")
);
storageService.fetchAndUpload(uris);
}
}
However, the following unit test does not successfully mock the restTemplate.getForObject call, it constantly returns null, even when setting both arguments to any().
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureWebClient
public class StorageServiceTest {
#MockBean
private AmazonS3 s3Client;
#MockBean
private RestTemplate restTemplate;
#MockBean
private SignService signingService;
#Autowired
// #InjectMocks ???
private StorageService storageService;
#Value("${app.aws.s3.bucket}")
private String bucket;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
List<URI> randomUris(int num) {
final String base = "https://example.com/%s";
return Stream.iterate(0, i -> i)
.limit(num)
.map(o -> URI.create(String.format(base, UUID.randomUUID().toString())))
.collect(Collectors.toList());
}
#Test
public void fetchAsyncTest() {
List<URI> uris = randomUris(2);
uris.forEach(uri -> {
ByteArrayInputStream data = new ByteArrayInputStream(
Integer.toString(uri.hashCode()).getBytes());
PutObjectRequest request = new PutObjectRequest(
bucket, getName(uri.toString()), data, new ObjectMetadata());
Resource getResult = mock(Resource.class);
try {
doReturn(data).when(getResult).getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
doReturn(data).when(restTemplate).getForObject(any(), any());
// none are working
// doReturn(data).when(restTemplate).getForObject(eq(uri), eq(Resource.class));
// doReturn(data).when(restTemplate).getForObject(uri, Resource.class);
// when(restTemplate.getForObject(uri, Resource.class)).thenReturn(data);
// when(restTemplate.getForObject(eq(uri), eq(Resource.class))).thenReturn(data);
// when(restTemplate.getForObject(any(), any())).thenReturn(data);
PutObjectResult res = new PutObjectResult();
doReturn(res).when(s3Client).putObject(eq(request));
// not working here as well, i think
// doReturn(res).when(s3Client).putObject(request);
// doReturn(res).when(s3Client).putObject(any());
// when(s3Client.putObject(eq(request))).thenReturn(res);
// when(s3Client.putObject(request)).thenReturn(res);
// when(s3Client.putObject(any())).thenReturn(res);
});
boolean res = storageService.fetchAndUpload(uris);
}
}
And just in case it's relevant, this is how I'm building the RestTemplate:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
I'm stumped, all advice is appreciated! :|
To follow up on this - my problem was because the method I was attempting to test was marked with Spring Boot's #Async annotation, causing race conditions and some mocks to not be configured properly.
I have some spring service that can submit some AWS batch job. This is simple spring batch job that invoke requst to external service. And i want to propagate traceId that generated in my service by including "org.springframework.cloud:spring-cloud-starter-sleuth" lib into classpath , to this job and add "TraceRestTemplateInterceptor" interceptor to external request initilaized with this traceId.
How can i do that? How can i initilaze interceptor which will put existing traceId from application parameter, environment, properties?
Or may be need to create some configuration beans?
UPDATE:
Simplified example:
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(DemoApplication.class);
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
//#Autowired
//RestTemplate restTemplate;
#Override
public void run(String... args) {
logger.info("Hello, world!");
//restTemplate.getForObject("some_url", String.class);
}
}
File application.properties:
x-b3-traceId=98519d97ce87553d
File build.gradle:
dependencies {
implementation('org.springframework.cloud:spring-cloud-starter-sleuth')
}
Output:
INFO [-,,,] 15048 --- [ main] com.example.demo.DemoApplication : Hello, world!
First of all, I want to see here traceId which initilized in application.properties. Secondly, when uncomment resttemplate clause, this traceId propagated into request.
Is it possible?
Resolved this issue only by manually putting into request HEADER key "X-B3-TRACEID" with corresponding value, which is inserted by external application as system property when submits target spring boot application. And manually inserting this key in MDC. Example, this snipet from spring boot application that must get traceId and propagate:
#Bean
public void setTraceIdToMDC(#Value("${x.b3.traceid}") String traceId) {
MDC.put("x-b3-traceId", traceId);
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Bean
public CommandLineRunner commandLineRunnerer(RestTemplate restTemplate, #Value("${x.b3.traceid}") String traceId) {
return args -> {
MultiValueMap<String, String> header = new LinkedMultiValueMap<>();
header.add("X-B3-TRACEID", traceId);
HttpEntity httpEntity = new HttpEntity(header);
logger.info("Execute some request"); //<-- prints expected traceId
restTemplate.exchange("some_url", HttpMethod.GET, httpEntity, String.class);
};
}
You can get the bean:
#Autowired
private Tracer tracer;
And get the traceId with
tracer.getCurrentSpan().traceIdString();
Just add the dependency to the classpath and set rest template as a bean. That's enough.
As spring sleuth doesn't support Webservicetemplate by default, here is an example of how to use spring cloud sleuth with Webservicetemplate,
if service A sends a request to service B,
At first you'll send the trace id in the header of the sent request by the below code
#Service
public class WebServiceMessageCallbackImpl implements WebServiceMessageCallback {
#Autowired
private Tracer tracer;
public void doWithMessage(WebServiceMessage webServiceMessage) throws TransformerException {
Span span = tracer.currentSpan();
String traceId = span.context().traceId();
SoapMessage soapMessage = (SoapMessage) webServiceMessage;
SoapHeader header = soapMessage.getSoapHeader();
StringSource headerSource = new StringSource("<traceId>" + traceId + "</traceId>");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
}
}
then in service B, you'll create an interceptor, then read the trace id from the header of the coming request, then put this trace id in the MDC like in the below code
#Slf4j
#Component
public class HttpInterceptor2 extends OncePerRequestFilter {
private final String traceId = "traceId";
#Autowired
private Tracer tracer;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String payload = new String(request.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
String traceId = traceId(payload);
MDC.put("traceId", traceId);
try {
chain.doFilter(request, response);
} finally {
MDC.remove(traceId);
}
}
private String traceId(String payload) {
StringBuilder token = new StringBuilder();
if (payload.contains(traceId)) {
int index = payload.indexOf(traceId);
while (index < payload.length() && payload.charAt(index) != '>') {
index++;
}
index++;
for (int i = index; ; i++) {
if (payload.charAt(i) == '<') {
break;
}
token.append(payload.charAt(i));
}
}
if (token.toString().trim().isEmpty()) {
token.append(traceId());
}
return token.toString().trim();
}
private String traceId() {
Span span = tracer.currentSpan();
String traceId = span.context().traceId();
return traceId;
}
}
I would like to load a different set of messages_xx.properties based on the HttpServletRequest to differentiate them based on our customers.
In templates and through all the application, we have a #Bean which gives the actual customer based on the path of the request
#Component
public class CompanySelector {
#Autowired
private ICompanyService service;
public String getURLBase(HttpServletRequest request) throws MalformedURLException {
URL requestURL = new URL(request.getRequestURL().toString());
String port = requestURL.getPort() == -1 ? "" : ":" + requestURL.getPort();
return requestURL.getHost() + port;
}
public Company getActualCompany(HttpServletRequest request) throws MalformedURLException{
String url = getURLBase(request);
Company company = service.findByCompanyUrl(url);
if(company != null){
return company;
}
return null;
}
}
Now, we configure the MessageSource in WebConfig which extends WebMvcConfigurerAdapter and we would like to do something like that
#Configuration
#ComponentScan("it.besmart.eshare.web")
public class WebConfig extends WebMvcConfigurerAdapter{
public WebConfig(){
super();
}
#Autowired
CompanySelector companySelector;
#Autowired
HttpServletRequest request;
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
Company company = null;
try {
company = companySelector.getActualCompany(request);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (company != null){
messageSource.setBasename("classpath:"+ company.getSlug() + "_messages");
} else {
messageSource.setBasename("classpath:messages");
}
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
but obviously it doesn't work because we don't have a request during configuration... Is there another way to load the messages file based on the request? Or any other best practice to adopt? Because our other choice would be using only one file per language and using the company.getSlug() at the beginning of each phrase, but we would decuplicate the size of file...
You need to declare every properties files like that:
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("company1_messages", "company2_messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
And to get the message:
#Autowired
private MessageSource messageSource;
public String myRequest(Locale locale) {
...
messageSource.getMessage(company.getSlug().".messageKey1", null, locale));
...
}
In company1_messages_fr.properties:
company1.messageKey1=messageCompany1
In company2_messages_fr.properties:
company2.messageKey1=messageCompany2