If my understanding is correct, you can add/remove params for saveEmployee() freely. For example, when you add "loc" as follows, saveEmployee() receives the "non-null object" when the event happens. And the same goes for queryParams.
#Controller
public class Employee {
#RequestMapping("/save")
public void saveEmployee(Locale loc,
#RequestParam Map<String, String> queryParams) {
// saving employee
}
}
How could this method receive non-null Locale object by just adding a param "loc" here?
I would like to know the logic behind this.
Spring does it for you by using LocaleResolver or LocaleContextResolver, for the current request locale, determined by the most specific locale resolver available, in effect, the configured LocaleResolver / LocaleContextResolver in an MVC environment.
21.3.3 Defining #RequestMapping handler methods
An #RequestMapping handler method can have a very flexible signatures. The supported method arguments and return values are described in the following section. Most arguments can be used in arbitrary order with the only exception of BindingResult arguments.
Supported method argument types
java.util.Locale for the current request locale, determined by the most specific locale resolver available, in effect, the configured LocaleResolver / LocaleContextResolver in an MVC environment.
Spring's DispatcherServlet which forwards request from client to your controller gives you that parameters. In order to do that, it search the object from ApplicationContext to which bean(Controller) belongs.
Spring looks at the method arguments, their types and annotations, then determines if it can provide an object of that type/annotation.
If it cannot, it'll throw an exception, otherwise it will call the method with the object it decided fits the type/annotation.
For the list of supported types/annotations, read the documentation:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-arguments
As you can see, java.util.Locale is listed.
I think you need xml setting for your locale
Do you want to try the followning xml setting
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en" />
</bean>
<mvc:interceptors>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="language" />
</bean>
</mvc:interceptors>
I'm trying to understand how spring mvc generates a markup. For instance, consider the simple controller:
#Controller
public class HelloController{
#RequestMapping("/hello")
public String hello(){
return "hello";
}
}
and say, that we're applying UrlBasedViewResolver defined in the dispatcher-servlet.xml as follows:
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="prefix" value="/WEB-INF/views/">
<property name="suffix" value="jsp">
</bean>
What the javadoc of the resolver does is says that we have three methods that return the instance of the View interface.
The first one is
protected AbstractUrlBasedView buildView(String viewName),
the second is
protected View createView(String viewName, Locale locale)
and the third is
protected View loadView(String viewName, Locale locale).
As long as the View interface has the method render(Map<String,?> model, HttpServletRequest request, HttpServletResponse response) I'd assume that once the instance of View has been created we call this method to render the markup to the client. But I'm not sure if it actually works that way.
In general, my question is what method takes the a jsp-page and return the instance of View to be rendered to the client.
In general, my question is what method takes the a jsp-page and return the instance of View to be rendered to the client.
In the case of UrlBasedViewResolver, that would be the createView method, which for a JSP will return an InternalResourceView.
As far as the view resolution framework is concerned, the ViewResolver interface is the entry point, and has a method resolveViewName which takes the view name ("hello" in your example") and returns a View object, then calls render on that.
The buildView, createView and loadView methods are all internal specific to the UrlBasedViewResolver implementation of ViewResolver.
I am trying to write a simple webapp, which takes email id as parameter and generates a token for the user with that id. I think my code is self-explanatory, so I will just paste it here so that I don't have to explain in detail.
This is my controller/servlet code
User user = userManager.getUserByEmailId("xyz#gmail.com");
if (user == null) {
//TODO handle this
}
if (user.getIssuedTokens() == user.getMaxTokens()) {
// TODO handle this
}
String token = tokenService.createToken();
user.setToken(token);
user.setIssuedTokens(user.getIssuedTokens() + 1);
userManager.updateUser(user);
userManager and tokenService are Service layer implementations.
#Service("tokenService")
public class TokenizationServiceImpl implements TokenizationService {
#Autowired
private TokenDAO tokenDAO;
#Transactional
public String createToken() {
String uuid = UUID.randomUUID().toString();
tokenDAO.createToken(uuid);
return uuid;
}
}
#Service("usermanager")
public class UserInterfaceImpl implements UserInterface {
#Autowired
private UserDAO userDAO;
#Transactional
public void createUser() {
userDAO.createUser();
}
public User getUserByEmailId(String emailID) {
return userDAO.getUserByEmailId(emailID);
}
#Transactional
public void updateUser(User user) {
userDAO.updateUser(user);
}
}
my spring configuration is like this
<tx:annotation-driven />
<context:component-scan base-package="com.myapp.dao" />
<context:component-scan base-package="com.myapp.service" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mydb" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!-- dataSource TransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
Here are my questions:
Does it make sense to inject two services (usermanager and tokenService) into my controller/servlet and then invoke them one after another? Or Should I have written one method in TokenServiceImpl directly, which would make use of usermanager service or UserDAO directly?
The transactional attribute did not seem to work for me. When updateUser method failed, a token was created in the database from createToken() method. What am I doing wrong here? Why did not the transaction rollback?
How do I in general decide on whether my service should use multiple DAOs or other services?
Well, my thoughts about your second question may also answer your first question. Looking to you snippet, I can notice that you are creating two different transactions for tokenService.createToken() and userManager.updateUser(user) since you are calling them from outside a #Transaction method. In order fix this behavior, you gonna need to do something like:
public class UserService {
...
#Transactional
public void assignToken() {
User user = userManager.getUserByEmailId("xyz#gmail.com");
if (user == null) {
//TODO handle this
}
if (user.getIssuedTokens() == user.getMaxTokens()) {
// TODO handle this
}
String token = tokenService.createToken();
user.setToken(token);
user.setIssuedTokens(user.getIssuedTokens() + 1);
userManager.updateUser(user);
}
}
You can notice that, in order to take into account this new transactional behavior, I created a new business component called UserService. Even though I don't really know your application enough to say what is the best approach, I would definitely not let it in your controller. In my opinion, you should isolate this behavior in:
a new business component (as I did in this example)
or encapsulate it inside your UserManager for instance
Now, is up to you decide whether it's worth coupling UserService to TokenizationService or creating a new business class for that. Reading the code you provided, it seems to me that UserService could have a TokenizationService since tokens won't be used in a different context.
Please, let me know your opinions.
Yes, all the code in the controller should be in a transactional service. As is your service doesn't do anything other than delegating to methods of the DAO. The service is supposed to contain the business logic, and to demarcate transactions.
Each of your services is transactional. So, when calling createToken(), a transaction starts and is committed as soon as createToken() returns. And when updateUser() is called, another transaction is started and commits or rollbacks as soon as the updateUser() returns/fails. That's one of the reasons why all the controller code should be in a single transactional service. If that was the case, both calls would be made in a single transaction, and if the second call failed, the whole transaction would be rollbacked, including the token generation.
My rule is: if a service just needs to get data from the database, it should use a DAO. If it needs to reuse business logic already defined in another service, then it should delegate to that service.
I'm attempting to forward a request received from one Controller to a second Controller using Spring's "forward:" prefix. However, instead of forwarding the request on to the second Controller as I would expect, the request is instead handled by the first Controller over and over again (until I receive a StackOverflowError). This made me think it might be treating the "/app/pong" as a relative path of some sort, but I'm not sure why this would be the case. Am I misunderstanding how "forward:" is supposed to work? Is there something that I'm missing here?
Controllers:
#Controller
public class ControllerOne {
#RequestMapping(value = "/ping", method = RequestMethod.GET)
public String doPing(HttpServletRequest request) {
log.debug("Ping?");
return "forward:/app/pong";
}
}
#Controller
public class ControllerTwo {
#RequestMapping(value = "/pong", method = RequestMethod.GET)
public String doPong(HttpServletRequest request) {
log.debug("Pong!");
return "pong";
}
}
servlet-mapping:
<servlet-mapping>
<servlet-name>test-servlet</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
view resolver:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
I think the problem is about the InternalResourceViewResolver that you configed. Because the inner logic of this viewResolver, it will do a forward automatically, you can config another kinds of viewResolver: UrlBasedViewResolver .
Below is the information I found in official document:
It is also possible to use a special forward: prefix for view names
that are ultimately resolved by UrlBasedViewResolver and subclasses.
This creates an InternalResourceView (which ultimately does a
RequestDispatcher.forward()) around the rest of the view name, which
is considered a URL. Therefore, this prefix is not useful with
InternalResourceViewResolver and InternalResourceView (for JSPs for example). But the prefix can be helpful when you are primarily
using another view technology, but still want to force a forward of a
resource to be handled by the Servlet/JSP engine. (Note that you may
also chain multiple view resolvers, instead.)
I have a controller that provides RESTful access to information:
#RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}")
public ModelAndView getBlah(#PathVariable String blahName, HttpServletRequest request,
HttpServletResponse response) {
The problem I am experiencing is that if I hit the server with a path variable with special characters it gets truncated. For example:
http://localhost:8080/blah-server/blah/get/blah2010.08.19-02:25:47
The parameter blahName will be blah2010.08
However, the call to request.getRequestURI() contains all the information passed in.
Any idea how to prevent Spring from truncating the #PathVariable?
Try a regular expression for the #RequestMapping argument:
RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName:.+}")
This is probably closely related to SPR-6164. Briefly, the framework tries to apply some smarts to the URI interpretation, removing what it thinks are file extensions. This would have the effect of turning blah2010.08.19-02:25:47 into blah2010.08, since it thinks the .19-02:25:47 is a file extension.
As described in the linked issue, you can disable this behaviour by declaring your own DefaultAnnotationHandlerMapping bean in the app context, and setting its useDefaultSuffixPattern property to false. This will override the default behaviour, and stop it molesting your data.
Spring considers that anything behind the last dot is a file extension such as .jsonor .xml and truncate it to retrieve your parameter.
So if you have /{blahName}:
/param, /param.json, /param.xml or /param.anything will result in a param with value param
/param.value.json, /param.value.xml or /param.value.anything will result in a param with value param.value
If you change your mapping to /{blahName:.+} as suggested, any dot, including the last one, will be considered as part of your parameter:
/param will result in a param with value param
/param.json will result in a param with value param.json
/param.xml will result in a param with value param.xml
/param.anything will result in a param with value param.anything
/param.value.json will result in a param with value param.value.json
...
If you don't care of extension recognition, you can disable it by overriding mvc:annotation-driven automagic:
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<property name="useSuffixPatternMatch" value="false"/>
</bean>
So, again, if you have /{blahName}:
/param, /param.json, /param.xml or /param.anything will result in a param with value param
/param.value.json, /param.value.xml or /param.value.anything will result in a param with value param.value
Note: the difference from the default config is visible only if you have a mapping like /something.{blahName}. See Resthub project issue.
If you want to keep extension management, since Spring 3.2 you can also set the useRegisteredSuffixPatternMatch property of RequestMappingHandlerMapping bean in order to keep suffixPattern recognition activated but limited to registered extension.
Here you define only json and xml extensions:
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="contentNegotiationManager" ref="contentNegotiationManager"/>
<property name="useRegisteredSuffixPatternMatch" value="true"/>
</bean>
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false"/>
<property name="favorParameter" value="true"/>
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
Note that mvc:annotation-driven accepts now a contentNegotiation option to provide a custom bean but the property of RequestMappingHandlerMapping has to be changed to true (default false) (cf. https://jira.springsource.org/browse/SPR-7632).
For that reason, you still have to override all the mvc:annotation-driven configuration. I opened a ticket to Spring to ask for a custom RequestMappingHandlerMapping: https://jira.springsource.org/browse/SPR-11253. Please vote if you are interested in.
While overriding, be careful to consider also custom Execution management overriding. Otherwise, all your custom Exception mappings will fail. You will have to reuse messageCoverters with a list bean:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<util:list id="messageConverters">
<bean class="your.custom.message.converter.IfAny"></bean>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"></bean>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</util:list>
<bean name="exceptionHandlerExceptionResolver"
class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
<property name="order" value="0"/>
<property name="messageConverters" ref="messageConverters"/>
</bean>
<bean name="handlerAdapter"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService" />
<property name="validator" ref="validator" />
</bean>
</property>
<property name="messageConverters" ref="messageConverters"/>
</bean>
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
I implemented, in the open source project Resthub that I am part of, a set of tests on these subjects: see https://github.com/resthub/resthub-spring-stack/pull/219/files and https://github.com/resthub/resthub-spring-stack/issues/217
Everything after the last dot is interpreted as file extension and cut off by default.
In your spring config xml you can add DefaultAnnotationHandlerMapping and set useDefaultSuffixPattern to false (default is true).
So open your spring xml mvc-config.xml (or however it is called) and add
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false" />
</bean>
Now your #PathVariable blahName (and all other, too) should contain the full name including all dots.
EDIT: Here is a link to the spring api
Using the correct Java configuration class :
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter
{
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
{
configurer.favorPathExtension(false);
}
#Override
public void configurePathMatch(PathMatchConfigurer configurer)
{
configurer.setUseSuffixPatternMatch(false);
}
}
I also ran into the same issue, and setting the property to false didn't help me either. However, the API says:
Note that paths which include a ".xxx" suffix or end with "/" already
will not be transformed using the default suffix pattern in any case.
I tried adding "/end" to my RESTful URL, and the problem went away. I'm not please with the solution, but it did work.
BTW, I don't know what the Spring designers were thinking when they added this "feature" and then turned it on by default. IMHO, it should be removed.
I resolved by this hack
1) Added HttpServletRequest in #PathVariable like below
#PathVariable("requestParam") String requestParam, HttpServletRequest request) throws Exception {
2) Get the URL directly (At this level no truncation) in the request
request.getPathInfo()
Spring MVC #PathVariable with dot (.) is getting truncated
adding the ":.+" worked for me, but not until I removed outer curly brackets.
value = {"/username/{id:.+}"} didn't work
value = "/username/{id:.+}" works
Hope I helped someone :]
I just ran into this and the solutions here didn't generally work as I expected.
I suggest using a SpEL expression and multiple mappings, e.g.
#RequestMapping(method = RequestMethod.GET,
value = {Routes.BLAH_GET + "/{blahName:.+}",
Routes.BLAH_GET + "/{blahName}/"})
The file extension problem only exists if the parameter is in the last part of the URL. Change
#RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}")
to
#RequestMapping(
method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/safe")
and all will be well again-
If you can edit the address that requests are sent to, simple fix would be to add a trailing slash to them (and also in the #RequestMapping value):
/path/{variable}/
so the mapping would look like:
RequestMapping(method = RequestMethod.GET, value = Routes.BLAH_GET + "/{blahName}/")
See also Spring MVC #PathVariable with dot (.) is getting truncated.
//in your xml dispatcher add this property to your default annotation mapper bean as follow
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true"></property>
</bean>
The problem that you are facing is due to spring interpreting the last part of the uri after the dot (.) as a file extension like .json or .xml . So when spring tries to resolve the path variable it simply truncates the rest of the data after it encounters a dot (.) at the end of the uri.
Note: also this happens only if you keep the path variable at the end of the uri.
For example consider uri : https://localhost/example/gallery.df/link.ar
#RestController
public class CustomController {
#GetMapping("/example/{firstValue}/{secondValue}")
public void example(#PathVariable("firstValue") String firstValue,
#PathVariable("secondValue") String secondValue) {
// ...
}
}
In the above url firstValue = "gallery.df" and secondValue="link" , the last bit after the . gets truncated when the path variable gets interpreted.
So, to prevent this there is two possible ways:
1.) Using a regexp mapping
Use a regex at the end part of mapping
#GetMapping("/example/{firstValue}/{secondValue:.+}")
public void example(
#PathVariable("firstValue") String firstValue,
#PathVariable("secondValue") String secondValue) {
//...
}
By using + , we indicate any value after the dot will also be part of the path variable.
2.) Adding a slash at the end of our #PathVariable
#GetMapping("/example/{firstValue}/{secondValue}/")
public void example(
#PathVariable("firstValue") String firstValue,
#PathVariable("secondValue") String secondValue) {
//...
}
This will enclose our second variable protecting it from Spring’s default behavior.
3) By overriding Spring's default webmvc configuration
Spring provides ways to override the default configurations that gets imported by using the annotations #EnableWebMvc.We can customize the Spring MVC configuration by declaring our own DefaultAnnotationHandlerMapping bean in the application context and setting its useDefaultSuffixPattern property to false.
Example:
#Configuration
public class CustomWebConfiguration extends WebMvcConfigurationSupport {
#Bean
public RequestMappingHandlerMapping
requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping
= super.requestMappingHandlerMapping();
handlerMapping.setUseSuffixPatternMatch(false);
return handlerMapping;
}
}
Keep in mind that overriding this default configuration, affects all urls.
Note : here we are extending the WebMvcConfigurationSupport class to override the default methods. There is one more way to override the deault configurations by implementing the WebMvcConfigurer interface.
For more details on this read : https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/EnableWebMvc.html
Java based configuration solution to prevent truncation (using a not-deprecated class):
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
#Configuration
public class PolRepWebConfig extends WebMvcConfigurationSupport {
#Override
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
final RequestMappingHandlerMapping handlerMapping = super
.requestMappingHandlerMapping();
// disable the truncation after .
handlerMapping.setUseSuffixPatternMatch(false);
// disable the truncation after ;
handlerMapping.setRemoveSemicolonContent(false);
return handlerMapping;
}
}
Source: http://www.javacodegeeks.com/2013/01/spring-mvc-customizing-requestmappinghandlermapping.html
UPDATE:
I realized having some problems with Spring Boot auto-configuration when I used the approach above (some auto-configuration doesn't get effective).
Instead, I started to use the BeanPostProcessor approach. It seemed to be working better.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
private static final Logger logger = LoggerFactory
.getLogger(MyBeanPostProcessor.class);
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
setRemoveSemicolonContent((RequestMappingHandlerMapping) bean,
beanName);
setUseSuffixPatternMatch((RequestMappingHandlerMapping) bean,
beanName);
}
return bean;
}
private void setRemoveSemicolonContent(
RequestMappingHandlerMapping requestMappingHandlerMapping,
String beanName) {
logger.info(
"Setting 'RemoveSemicolonContent' on 'RequestMappingHandlerMapping'-bean to false. Bean name: {}",
beanName);
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
}
private void setUseSuffixPatternMatch(
RequestMappingHandlerMapping requestMappingHandlerMapping,
String beanName) {
logger.info(
"Setting 'UseSuffixPatternMatch' on 'RequestMappingHandlerMapping'-bean to false. Bean name: {}",
beanName);
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
}
}
Inspired from: http://ronaldxq.blogspot.com/2014/10/spring-mvc-setting-alwaysusefullpath-on.html
if you are sure that your text will not match any of default extensions you can use below code:
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseRegisteredSuffixPatternMatch(true);
}
}
My preferable solution to prevent the Spring MVC #PathVariable to get truncated is to add trailing slash at the end of the path variable.
For example:
#RequestMapping(value ="/email/{email}/")
So, the request will look like:
http://localhost:8080/api/email/test#test.com/