i18n translation in JSP custom tag - spring

Is it possible to write a custom JSP tag to take an i18n message key and output the translation phrase for the given request?
Normally in JSP/JSTL, I do:
<fmt:message key="${messageKey}"><fmt:param>arg1</fmt:param></fmt:message>
And I get the translation phrase. Now I need to do the following (there's a good reason for this):
<custom:translate key="${messageKey}" arg="arg1"/>
But I don't know how to look up the translation in the custom tag code. The TagSupport base class provides a pageContext from which I can get a ServletRequest which has the Locale... but how do I then look up the translation for a key?
I use Spring 3.0 and in my application-context.xml, I've defined a ReloadableBundleMessageSource so I can call:
messageSource.getMessage(
key, new Object[] {arg}, pageContext.getRequest().getLocale()
);
but I don't think I can inject messageSource into a custom tag, can I? Otherwise I can instantiate a new one, but would it load my tens of thousands of translations for every call? I don't want to resort to making messageSource a static member of a static class.

I don't do Spring, but in "plain" JSP you can just put the ResourceBundle instance in the session scope with help of a Filter or Servlet
ResourceBundle bundle = ResourceBundle.getBundle(basename, request.getLocale());
request.getSession().setAttribute("bundle", bundle);
And treat it in JSP like any other bean in EL.
${bundle[messageKey]}
It must be possible to have Spring to put that as a bean in the session scope.

There is a utility in spring to access the web application context. Then you can look up a bean by its name or type.
To get hold of your resource bundle, you could do something like:
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(pageContext.getServletContext());
messageResource = springContext.getBean("messageResource");

This question is very old but I think it is worth to share another way to solve this.
To access the Spring message source in a custom tag you only need to extend the class org.springframework.web.servlet.tags.RequestContextAwareTag instead of TagSupport.
In this case you have to implement the method doStartTagInternal() instead of doStartTag(), but inside this method you will have access to the MessageSource through getRequestContext().getMessageSource() method.
Therefore, your class would look like:
public class CreateCustomFieldTag extends RequestContextAwareTag{
//variables (key, arg...), getters and setters
#Override
protected int doStartTagInternal() throws Exception {
getRequestContext().getMessageSource().getMessage(
key, new Object[] {arg}, getRequestContext().getLocale());
}
}

Related

Spring inject component into non-spring managed interface/abstract class and its subclasses

TLDR: I need an interface/abstract class and all classes implementing it to have access to a Spring managed bean. Can Spring inject a bean into an interface/abstract-class and its subclasses simply via #Autowired ?
I am working on an API built with Spring Webflux + Cloud Gateway that depending on the cookie JWT authorized party, identifies the User's policy group and assign an Attribute ENUM "InterfaceID" to the ServerWebExchange via exchange.getAttribute().put("InterfaceID",InterfaceID.A) after the JWT is validated, and currently uses "InterfaceID" to represent the different groups of users/different interface the user entered from.
JWTValidationFilter.java [Current]
switch(JWTValidator.validate(jwt).get("AZP")){
//if user is from company A or its partners
case "a":
case "aa":
exchange.getAttribute().put(InterfaceID.COMPANY_A_ACCESS);
break;
case "b":
exchange.getAttribute().put(InterfaceID.NORMAL_ACCESS);
...
}
For certain API endpoints (say /api/getSessionDocument), different "InterfaceID" fetches data from different DB/apis, as well as have different permission checking on top of that.
RequestController.java [Current]
#Autowired
APICallerUtil apiCallerUtil;
switch(exchange.getAttribute.get(InterfaceID)){
case "NORMAL_ACCESS":
apiCallerUtil.getDataFromApiA();
break;
case "COMPANY_A_ACCESS":
// call api B but check for permission from api D first
...
}
The endpoint's controller now has another switch statement, and to many code analyzers this have been a code smell. I have been trying to refactor this entire bit of code to use polymorphism to handle the different "getSessionDocument" flows, but i run into issues regarding the injection of util classes that calls specific APIs.
APICallerUtil.java class, exisiting class from the project, would prefer not to refactor this.
#Component
public class APICallerUtil{
#Value("${some uri to some API}") //different by environment and therefore cant be static final
private String uri1;
#Value("${some auth to some API}") //confidential
private String uri1AuthHeader;
//...
public JSONObject getDataFromApiA(String somekey){ //cant be static since uri1 is not static
//Some code that uses uri1 and apache httpclient
return data;
}
...
}
IBaseAccess.java
interface IBaseAccess{
default Mono<JSONObject> getSesssionDocument(ServerWebExchange e){return Mono.error("not implemented");}
}
RequestController.java [new]
#Autowired
APICallerUtil apiCallerUtil;
return exchange.getAttribute.get(InterfaceID).getSessionDocument(exchange);
NormalAccess.java
public class NormalAccess implements IBaseAccess{
//can i autowire APICallerUtil here?
//use constructor to pass the Util class reference here?
Mono<JSONObject> getSesssionDocument(ServerWebExchange e){
//need to call ApiA here
//need to call ApiC here
}
}
NormalAccess needs to call APICaller.getDataFromApiA(), but it needs a reference to the Spring managed instance of APICaller. What would be the "correct" way to pass the reference/autowire API caller into NormalAccess, or even better IBaseAccess (so that the implementing classes can use the Util bean)?
JWTValidationFilter.java [new]
switch(JWTValidator.validate(jwt).get("AZP")){
//if user is from company A or its partners
case "a":
case "aa":
exchange.getAttribute().put("InterfaceID",new CompanyAAccess(/*pass the util class here?*/));
break;
case "b":
exchange.getAttribute().put("InterfaceID",new NormalAccess(/*pass the util class here?*/));
...
}
I have tried several methods, but either I lack the knowledge on the specific Spring feature, or that method is deeemed a bad design choice by some, including:
Making the methods and fields in APICallerUtil static, via suggestions from Spring: How to inject a value to static field? and Assigning private static final field member using spring injection , then the Access classes can call the static methods.
Creating a contructor for IBaseAccess that consumes the APICallerUtil reference and store it inside. The JWTfilter would hold an autowired APICallerUtil and pass it in when the attribute is assigned.
Create a static class that provides the application context and Access classes use applicationContext.getBean("APICallerUtil"); to obtain the bean.
Use the #Configurable annotation? I could not find much documentation on how this works for interfaces/abstract-class.
I understand that there might not exist an absolute answer for this question, but regardless I'd like suggestion/feedback on which of these approaches are viable/good. Especailly concerning whether the APIUtil class should be static or not.

Replacing micronaut's default KafkaProducerFactory with custom factory implementation

I need to customize the default KafkaProducerFactory (or any other default factory, say KafkaConsumerFactory) that ships with micronaut-kafka dependency. For that I tried to replace the existing factory using,
#Factory
#Replaces(factory = KafkaProducerFactory.class)
class CustomFactory extends KafkaProducerFactory {
#Bean
#Any
public <K, V> Producer<K, V> getProducer(
#Nullable InjectionPoint<KafkaProducer<K, V>> injectionPoint,
#Nullable #Parameter AbstractKafkaProducerConfiguration<K, V> producerConfiguration) {
validate(producerConfiguration); //this is my primary intension
super.getProducer(injectionPoint, producerConfiguration);
}
}
But it seems that Micronaut is not able to replace KafkaProducerFactory hence both the factory exists and I am getting error saying
"multiple candidate bean exists [CustomFactory, KafkaProducerFactory]"
I also thought to exclude the KafkaProducerFactory while the application loads, but could not find anything similar to Spring's ComponentScan.excludeFilter in Micronaut.
Is there anything wrong in my configuration or is there any other way to achieve the same?
Finally I got the answer. Let me elaborate little more for the actual context,
Problem
We have custom way of creating producer/consumer instances i.e. a custom class that creates those given the config properties. Now I had to modify the default factories so that instead of creating the instances on its own, the factory should invoke our custom class to instantiate producer/consumers.
Solution
I had to add #Primary along with other annotations and its working,
#Factory
#Replaces(factory = KafkaProducerFactory.class)
#Primary
class CustomFactory extends KafkaProducerFactory {
//code here
}
But the way I acheived this is kind of a work around because,
my primary intension was to override the producer/consumer creation part of default factory in a sub class and then replacing the default factory by the sub class. But as per the code structure in default factory class, it was not a single place where we can plug our custom code (no specific public method present consolidating the code for creating producer/consumer, it was being created from 3 separate places with "new") to create the producer. Hence we had to copy the entire default factory class and replaced the 3 places with custom code which does not seem to be a correct way.

Spring return dynamic instance based of String value

Java Spring question:
I have a interface MyInterface with one method
void exec (String str);
I have many implementation of MyInterface, say Oneimpl, anotherimpl yetanotherimpl...and so on and can keep adding new implementations.
how do I obtain an instance of a specific implementation using just the name of the implementing class passed as a STRING value , say "someRandomImpl"
The code should be dynamic and can provide a instance of new implementations without code change.
implements ApplicationContextAware
it will autowired ApplicationContext object
use the object like
context.getBean(beanName)
then you get the bean

Configuring Spring MockMvc to use custom argument resolver before built-in ones

I have a straightforward test case. I have a controller which has a parameter of a type Spring doesn't support by default, so I wrote a custom resolver.
I create the mock mvc instance I'm using like so:
mvc = MockMvcBuilders.standaloneSetup(controller).setCustomArgumentResolvers(new GoogleOAuthUserResolver()).build();
However, Spring is also registering almost 30 other argument resolvers, one of which is general enough that it is getting used to resolve the argument before mine. How can I set or sort the resolvers so that mine is invoked first?
This worked for me without reflection:
#RequiredArgsConstructor
#Configuration
public class CustomerNumberArgumentResolverRegistration {
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#PostConstruct
public void prioritizeCustomArgumentResolver () {
final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(Objects.requireNonNull(requestMappingHandlerAdapter.getArgumentResolvers()));
argumentResolvers.add(0, new CustomerNumberArgumentResolver());
requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers);
}
}
The issue was that the People class the Google OAuth library I am using extends Map and the mock servlet API provides no way to manipulate the order in which the handlers are registered.
I ended up using reflection to reach into the mocks guts and remove the offending handler.

Spring - binding to an object rather than a String or primitive

Let's say I have the following command object:
class BreakfastSelectCommand{
List<Breakfast> possibleBreakfasts;
Breakfast selectedBreakfast;
}
How can I have spring populate "selectedBreakfast" with a breakfast from the list?
I was figuring I'd do something like this in my jsp:
<form:radiobuttons items="${possibleBreakfasts}" path="selectedBreakfast" />
But this doesn't seem to work. Any ideas?
thanks,
-Morgan
The key to it all of this is the PropertyEditor.
You need to define a PropertyEditor for your Breakfast class and then configure the ServletDataBinder using registerCustomEditor in the initBinder method of your controller.
example:
public class BreakfastPropertyEditor extends PropertyEditorSupport{
public void setAsText(String incomming){
Breakfast b = yourDao.findById( Integer.parseInt(incomming));
setValue(b);
}
public String getAsText(){
return ((Breakfast)getValue()).getId();
}
}
note you'll be needing some null checking etc, but you get the idea. In your controller:
public BreakfastFooBarController extends SimpleFormController {
#Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(Breakfast.class, new BreakfastPropertyEditor(yourDao));
}
}
things to watch out for:
PropertyEditor's are not thread safe
if you need spring beans, either manually inject them or define them in spring as prototype scope and use method injection into your controller
throw IllegalArgumentException if the inbound parameter is not valid/not found, spring will convert this into a binding error correctly
hope this helps.
Edit (in response to the comment):
It looks a little strange in the given example because BreakfastSelectCommand doesn't look like an entity, I'm not sure what the actual scenario you have is. Say it is an entity, for example like Person with a breakfast property then the formBackingObject() method would load the Person object from the the PersonDao and return it as the command. The binding phase would then change the breakfast property depending on the selected value, such that the command that arrives in onSubmit has the breakfast property all set up.
Depending on the implementation of your DAO objects calling them twice or attempting to load the same entity twice doesn't actually mean that you will get two SQL statements being run. This applies particularly to Hibernate, where it guarantees that it will return the same object that is in it's session for a given identifier, thus running letting the binding attempt to load the Breakfast selection even through it hasn't changed shouldn't result in any undue overhead.

Resources