Internationalized drop downs using Spring 3 - spring

Story
I have a select control that represents user access level. I'm looking for a way to internationalize it. The label should be loaded from a message resource and the value should be used as is. I prepare all my drop down lists in controllers using a simple SelectOption class that has a label and a value properties. This way, my select's look consistent accross all jsp's.
Problem
I've found some examples but they are based on logic within jsp. Developer loops through his labels and manually constructs the option tag using a message resource. While this works, there just has to be a better way. I've also found some comments that Spring 3 will have support for internationalizing option labels but I can't find anything concrete on that.
Controller logic
Collection<SelectOption> optionList = new ArrayList<SelectOption>();
optionList.add(new SelectOption("-SELECT-", "-"));
optionList.add(new SelectOption("Administrator", "ADMIN"));
optionList.add(new SelectOption("Editor", "EDIT"));
bean.setFilterUserAccessLevelOptionList(optionList);
JSP logic
<form:select path="filterUserAccessLevel" items="${bean.filterUserAccessLevelOptionList}" itemLabel="label" itemValue="value"/>
Questions
I would like to add options in my controller in this way: optionList.add(new SelectOption("userAccessLevelAdministratorLabel", "ADMIN")); and have Spring convert userAccessLevelAdministratorLabel to a value from a message resource. Is this possible?
If Spring 3 cannot do this for me, how else can this be achieved without manually constructing the option tag within jsp?
=== 2012-01-15 ==============================================================
Still trying to work out a solution using aweigold's idea.
Controller
#Controller
public class UserController {
#Autowired
private UserService userService;
#Autowired
SelectOptionListBuilder listBuilder;
#RequestMapping("/userIndex/{pageNumber}")
public ModelAndView getUserList(#PathVariable Integer pageNumber, #ModelAttribute("userIndexBean") UserIndexBean phantomBean, Locale locale, Model model) {
UserIndexBean bean = new UserIndexBean();
// prepare filter form
Collection<SelectOption> optionList = listBuilder.getUserAccessLevelOptionList(true, SortOrder.NONE, locale);
bean.setFilterUserAccessLevelOptionList(optionList);
SelectOptionListBuilderImpl
#Component
public class SelectOptionListBuilderImpl implements SelectOptionListBuilder, MessageSourceAware {
private MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#Override
public List<SelectOption> getUserAccessLevelOptionList(boolean addSelectPrompt, SortOrder sortOrder, Locale locale) {
List<SelectOption> optionList = new ArrayList<SelectOption>();
if(addSelectPrompt) {
optionList.add(new SelectOption(messageSource.getMessage("common.selectPromptLabel", null, locale), "-"));
}
messageSource mapping
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/i18n/messages" />
<property name="defaultEncoding" value="UTF-8"/>
<property name="UseCodeAsDefaultMessage" value="true"/>
</bean>
Exception
org.springframework.context.NoSuchMessageException: No message found under code 'common.selectPromptLabel' for locale 'en_CA'

When I need to do operations like this in a Controller outside of a jsp, I've been making my Controllers MessageSourceAware. Spring will then inject a new MessageSource when they are swapped, and you can interrogate it much like Spring does. In your example, you would do something like this:
#Controller
public class someController implements MessageSourceAware {
private MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#RequestMapping
// Pass in the locale from the LocaleResolver
public void someMapping(Locale locale){
optionList.add(new SelectOption(
messageSource.getMessage("userAccessLevelAdministratorLabel", null, locale),
"ADMIN"))
}
}

Have a look at a spring roo project. They managed this kind of problem by creating tagx tags. This tags do what you already descibed (it contains a litte logic to load the messages from ressources and build the option tags). But because the logic is witten once and you can use this tags like normal tags in you jspx files, it feels like a tag that do what you want to have.

Related

How to use custom expressions in Spring Security #PreAuthorize/#PostAuthorize annotations

Is there a way to create more expressive statements in #Preauthorize blocks? Here's an example of something I find myself repeating, because the #Preauthorize is not terribly smart out of the box.
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public void deleteGame(#PathVariable int id, #ModelAttribute User authenticatingUser) {
Game currentGame = gameService.findById(id);
if(authenticatingUser.isAdmin() || currentGame.getOwner().equals(authenticatingUser)) {
gameService.delete(gameService.findById(id));
} else {
throw new SecurityException("Only an admin, or an owner can delete a game.");
}
}
What I would prefer is something like.
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
#Preauthorize(isAdmin(authenicatingUser) OR isOwner(authenicatingUser, id)
public void deleteGame(#PathVariable int id, #ModelAttribute User authenticatingUser, #ModelAttribute currentGame ) { //I'm not sure how to add this either :(
gameService.delete(gameService.findById(id));
}
Part of the problem is that I need to make a query to the database to fetch some of this stuff to verify permissions, such as querying the database to get a copy of the game, and then comparing the owner of the game to the person making the request. I'm not really sure how all of that operates within the context of a #Preauthorize annotation processor, or how I add things to the collection of objects made available in the #Preauthorize("") value attribute.
Since #PreAuthorize evaluates SpEl-expressions, the easiest way is just to point to a bean:
#PreAuthorize("#mySecurityService.someFunction()")
MySecurityService.someFunction should have return type boolean.
Spring-security will automatically provide a variable named authentication if you want to pass the Authentication-object. You can also use any valid SpEl-expressions to access any arguments passed to your secure method, evaluate regular expressions, call static methods, etc. E.g:
#PreAuthorize("#mySecurityService.someFunction(authentication, #someParam)")
1) First you have to reimplement MethodSecurityExpressionRoot which contains extra method-specific functionality. The original Spring Security implementation is package private and hence it is not possible to just extend it. I suggest checking the source code for the given class.
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
// copy everything from the original Spring Security MethodSecurityExpressionRoot
// add your custom methods
public boolean isAdmin() {
// do whatever you need to do, e.g. delegate to other components
// hint: you can here directly access Authentication object
// via inherited authentication field
}
public boolean isOwner(Long id) {
// do whatever you need to do, e.g. delegate to other components
}
}
2) Next you have to implement custom MethodSecurityExpressionHandler that will use the above defined CustomMethodSecurityExpressionRoot.
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
#Override
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
}
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
MethodInvocation invocation) {
final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
3) Define expression handler bean in your context, e.g. via XML you can do it as follows
<bean id="methodSecurityExpressionHandler"
class="my.package.CustomMethodSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy" />
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
4) Register the above defined handler
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>
5) Then just use the defined expressions in your #PreAuthorize and/or #PostAuthorize annotations
#PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(#PathVariable int id, #ModelAttribute currentGame) {
// do whatever needed
}
And one more thing. It is not very common to use method level security to secure controller methods but rather to secure methods with business logic (a.k.a. your service layer methods). Then you could use something like the below.
public interface GameService {
// rest omitted
#PreAuthorize("principal.admin or #game.owner = principal.username")
public void delete(#P("game") Game game);
}
But keep in mind that this is just an example. It expects that the actual principal has isAdmin() method and that the game has getOwner() method returning username of the owner.
You could write your annotation something like:
#PreAuthorize("hasRole('ROLE_ADMIN') and hasPermission(#id, 'Game', 'DELETE')")
To get the hasPermission part working you need to implement PermissionEvaluator interface.
Then define an expression handler bean:
#Autowired
private PermissionEvaluator permissionEvaluator;
#Bean
public DefaultMethodSecurityExpressionHandler expressionHandler()
{
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
And inject in your security config:
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>

Mixing <mvc:annotation-driven conversion-service with #Autowired dependency coming back as null

I have a spring mvc 3.0 web application and am using a conversion-service as follows...
<bean id="applicationConversionService"
class="com.myer.reporting.converter.ApplicationConversionServiceFactoryBean"/>
<context:component-scan base-package="com.myer.reporting.controller" />
<context:component-scan base-package="com.myer.reporting.manager" />
<context:component-scan base-package="com.myer.reporting.dao"/>
<context:component-scan base-package="com.myer.reporting.dao.mapper"/>
<mvc:annotation-driven conversion-service="applicationConversionService"/>
In my controllers when i use the conversion service it all works fine.
#Autowired
private ApplicationConversionServiceFactoryBean applicationConversionService; '
But for the first time I am now trying to use the applicationConversionService in my mapper using the same method but the applicationConversionService is always coming back as null.
package com.myer.reporting.dao.mapper;
public class ImportHistoryRowMapper implements RowMapper<ImportHistory> {
private static final String BUSINESS_DATE = "businessdate";
private static final String TOTAL_TRAN_COUNT = "total_tran_count";
#Autowired
private ApplicationConversionServiceFactoryBean applicationConversionService;
public ImportHistory mapRow(ResultSet rs, int rowNum) throws SQLException {
Date businessDate = rs.getDate(BUSINESS_DATE);
Integer totalTranCount = rs.getInt(TOTAL_TRAN_COUNT);
ImportHistory importHistory = new ImportHistory();
importHistory.setBusinessDate(businessDate);
importHistory.setTransactionCount(totalTranCount);
// status
TrafficLightStatus status =
applicationConversionService.
getImportThresholdToStatusConverter()
.convert(totalTranCount);
importHistory.setStatus(status);
return importHistory;
}
}
I tried to add #Component to the top of the class but that doesn't seem to work either. I think I am lacking some understanding of the #Autowired annotation and therefore can't get this to work.
Can someone give me some advice on this please.
thanks
You need to make the ImportHistoryRowMapper spring managed for this to work.
I believe your dao class making the reference to this mapper class is singleton. For every request (i.e., call to method getJdbcTemplate().query ( sql, object array, new instance of ImportHistoryRowMapper);), you need to create new instance of ImportHistoryRowMapper i.e., it should be prototype.
You can inject prototype bean into singleton in 2 ways:
1) Lookup Method injection
2) Scoped proxies
Take a look at this.

How do I use Spring/JUnit to verify a controller is not sending me to a non-existent view?

I'm using Spring 3.1.1.RELEASE and JUnit 4.11. I setup my JUnit tests like so
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class MySpringTest
{
protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
protected MockHttpSession session;
#Autowired
protected RequestMappingHandlerAdapter handlerAdapter;
#Autowired
protected RequestMappingHandlerMapping handlerMapping;
When testing controllers, I have this line to verify that the view the controller's method is returning is the right view …
import static org.springframework.test.web.ModelAndViewAssert.assertViewName;
...
final ModelAndView mav = submitMyForm(…);
assertViewName(mav, "folder/myView");
...
protected ModelAndView submitMyForm(… params ...) throws Exception {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
request.setRequestURI("/myurl");
request.setMethod("POST");
request.addParameter("param1", param1);
...
final Object handler = handlerMapping.getHandler(request).getHandler();
return handlerAdapter.handle(request, response, handler);
}
My question is, once I verify the view returned my the controller is the expected view, how do I verify it won't result in a 404? The main problem I'm gaving now is testing whether or not the view actually maps to an underlying page in my WAR file.
why don't use spring-mvc-test and do something like this ?
#Autowired
private ViewResolver viewResolver;
// code
View view = viewResolver.resolveViewName(viewName, locale);
//assert view not null
or something like this, in wich you can check both if the view is ok and the returned status (is status 200/404?)
(more code here: http://goo.gl/fMqBsl)
#Test
public void indexTest() throws Exception {
mockMvc.perform(get("/")).andDo(print())
.andExpect(handler().handlerType(MainController.class))
.andExpect(handler().methodName("index"))
.andExpect(view().name("index"))
.andExpect(forwardedUrl("/WEB-INF/tiles/template.jsp"))
.andExpect(status().isOk());
}
i am using standard jsp view
basically, you need to know the view resolver(s). can a specific view be resolved? that means, if you DON'T have a file called abc.xml, it might still be a valid view.
for simplicity sake, lets assume that we have only one view resolver, and, its
"org.springframework.web.servlet.view.UrlBasedViewResolver"
and here is the bean definition
spring 3.2.4 documentation pdf, page 477
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
eg: the view name "page1" => /WEB-INF/jsp/page1.jsp and "admin/page2" => /WEB-INF/jsp/admin/page2.jsp
using this, you can Inject the view resolved to your junit test using #Autowired and/or #Qualifier
then read the "prefix" and suffix value and find the full path like "src/main/webapp/" + prefix + viewname + suffix
and check if the file exists.
you may have multiple view resolvers, so you may want to inject the context and handle the view => filename resolution using a strategy pattern.
something like
foreach resolver
{
if i can resolve the view to a file (resolver type, viewname)
return the physical filename
else
try next resolver
}

Using session attributes in spring MVC

I am developing a web application using spring MVC. I just want a simple example of how to do session management in this. I have seen lot of forums but I am not able to get a clear picture of this
My requirement is
I have an object, which I would like to be accessible in all controllers and JSP's I
would like to set that in the controller and get that in JSP
I am looking for something like
Session.setAtribute();
Could you please let me know a very simple instance . Thank you
There are different ways of accessing servlet session in Spring MVC. But I think this one is the one that best suits your problem. You can create a session scoped bean, which holds your desired info:
#Component("myObjectHolder")
#Scope(WebApplicationContext.SCOPE_SESSION)
public class MyObjectHolderImpl implements MyObjectHolder {
private long userId;
private String username;
private Theme theme;
// Getters & Setter
}
Then, you can access to it from other beans:
#Controller
public class MyController {
#Autowired private MyObjectHolder myObjectHolder;
#RequestMapping
public ModelAndView switchTheme(String themeId) {
...
Theme newTheme = themeService.get(themeId);
myObjectHolder.setTheme(newTheme);
...
}
}
You can access directly from your view too, but you must configure it:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
...
<property name="exposedContextBeanNames" value="myObjectHolder" />
</bean>
And in your JSP:
Hi ${myObjectHolder.username}, you switched
application theme to ${myObjectHolder.theme.name}
The simplest approach is to access HttpSession directly by injecting it into your handler method:
#RequestMapping("/page")
public ModelAndView page(HttpSession session) {
session.getAttribute("foo");
}

Need some explanation about BeanNameViewResolver

i read the documentation here:
http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/web/servlet/view/BeanNameViewResolver.html
but i think that the spring documentation sometimes can become complex and hard to understand, so i need little explanation about this class.
As described in the documentation, BeanNameViewResolver resolves Views declared as beans. Usually you need it for some special-purpose views.
Imagine, for example, that one of your controllers should render an Excel spreadsheet. So, you subclass AbstractExcelView and implement your custom logic to render a spreadsheet based on model values:
public class MyExcelView extends AbstractExcelView { ... }
and declare it as a bean:
<bean id = "myExcelView" class = "MyExcelView" />
Then declaring an BeanNameViewResolver makes it available to controllers: when controller returns ModelAndView with view name myExcelView, your spreadsheet will be rendered.
BeanNameViewResolver is usually used in conjunction with some other view resolver that handles "regular" views (so that if BeanNameViewResolver can't find a view, the other resolver tries to find it):
<bean class = "...BeanNameViewResolver">
<property name = "order" value = "0" />
</bean>
<bean class = "...InternalResourceViewResolver">
<property name = "order" value = "1" />
...
</bean>
Indeed the documentation is not fantastic.
The view resolver in spring mvc is a bean which translates from view names to views.
A view name is s simple string. It is returned by the controller's handleRequest() method within the ModelAndView object.
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
....
return ModelAndView("thisIsTheViewName", ...);
}
This view name is resolved to an actual view class by the view resolver.
The BeanNameViewResolver has an easy job: It looks for a view bean in the applicationContext which has this view name as its id.
<bean id="thisIsTheViewName" class="....MyView" />
For simple applications this can be the simplest way to translate from view name to view.
If you are looking for a very simple but complete example:
#Controller
public class MyController {
#GetMapping("/hello")
public String getHello() {
return "helloView";
}
}
#Component
public class HelloView extends AbstractView {
#Override
protected void renderMergedOutputModel(Map<String, Object> map,
HttpServletRequest req, HttpServletResponse res) throws Exception {
res.setContentType("text/plain");
res.getOutputStream().println("hello world");
}
}

Resources