====== Related to [[http://www.springsource.org/|Spring]] ====== * [[livejournal>ru-java/1033166|AOP на примере JSF 2 приложения]] * [[http://static.springsource.org/docs/Spring-MVC-step-by-step/|Developing a Spring Framework MVC application step-by-step]] * [[habrahabr>218203|Чем старше Spring, тем больше контекстов]] * [[http://www.javacodegeeks.com/2013/01/spring-property-placeholder-configurer-a-few-not-so-obvious-options.html|Spring Property Placeholder Configurer – A few not so obvious options (default values, recursive property resolution)]] * [[habrahabr>347816|Изучение Spring Framework 100% через практику. Активные туториалы и язык разметки KML]]. [[https://kciray8.github.io/KciTasks/App/src/|Проект KciTasks (beta)]]. ===== Questions answered ===== === Is it possible to set a bean property to given resource in module classpath as ''InputStream'' / as ''URL''? === Yes, this is possible due to build-in ''org.springframework.beans.propertyeditors.InputStreamEditor'', which allows conversion from ''String -> org.springframework.core.io.Resource -> java.io.InputStream''. If you have e.g. overloaded constructors, one of which has ''String'' argument, then you need to specify the target type explicitly: The same conversion is also possible via build-in ''org.springframework.beans.propertyeditors.URLEditor'', which allows conversion from ''String -> org.springframework.core.io.Resource -> java.net.URL'': === How to programmatically enumerate all classpath resources using Ant mask? === import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; for (Resource resource : new PathMatchingResourcePatternResolver().getResources("classpath*:/path/*.txt")) { resource.getInputStream(); } === [[stackoverflow>20084272|How to set ''null'' to a bean property?]] === Use SpEL: ''%%%%'' === Implementation of cache which is based on [[https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/LRUMap.html|Apache LRUMap]] === See {{:programming:java:lru_map_cache.7z}}. Usage: === Is it possible to set a bean property to given resource in module classpath as ''Property'' / as ''ResourceBundle''? === One alternative is to use ''PropertiesFactoryBean'' which allows you to load properties from text or XML file(s) and optionally merge with fixed values: or if you need to inject ''java.util.ResourceBundle'' type you may use ''org.springframework.beans.propertyeditors.ResourceBundleEditor'' (which is not registered in Spring context by default, so [[stackoverflow>1268021|one need to make it manually]]): More information is [[stackoverflow>317687|here]]. === How to override property declared in parent properties? === It is possible to pass an array of ''java.util.Properties'' objects to ''org.springframework.beans.factory.config.PropertiesFactoryBean'' which will be merged in given order. Note that under-the-hood below solution uses ''org.springframework.core.convert.support.StringToPropertiesConverter'' which converts from multi-line string to ''java.util.Properties''. /tmp 1000 size=10 === How to initialize the property value from system property or use the default value if system property is not defined? === Use SpEL: === How to collect and inject all beans of a given type? === From [[stackoverflow>2799316|How to collect and inject all beans of a given type in Spring]]: * ''@Autowired List list'' will work OK both for lists and arrays. * The analogue for XML configuration would be ''%%autowire="byType"%%'' or ''%%autowire="constructor"%%'' e.g. ''%%%%'' (see [[http://static.springsource.org/spring/docs/3.0.x/reference/beans.html#beans-factory-autowire|Autowiring collaborators]]). === [[stackoverflow>50448238|What is the Spring bean lifecycle?]] === {{https://www.concretepage.com/spring/images/spring-bean-life-cycle-tutorial.jpg}} === How to create deamon threads? === === How to call a method on specific bean? === * Method using ''MethodInvokingFactoryBean'': * Method using SpEL: ''#{listenerContainer.addListener(listener1)}''. === How to inject correctly prototype-scoped bean into singleton-scoped bean? === Doing this via ''@Autowired'' (or normal declarative bean injection via '''') will not result the desired effect, as in our singleton bean we want to get new version of prototype bean each time on request. The solutions are: * Wire the Spring context and retrieve the bean using the context (''%%@Autowired ApplicationContext context; ... context.getBean("prototypeBean");%%''). Drawback: explicit dependency on Spring classes. * Use [[springdocs>beans.html#beans-factory-lookup-method-injection|Lookup method injection]]. Mind that method to be overridden does not necessarily need to be ''abstract'' (normal ''protected'' method is fairly OK) as it will some difficulties to unit test the bean. Unfortunately, there is no way to configure this functionality using annotations prior to Spring v4.1 (see [[https://jira.springsource.org/browse/SPR-5192|SPR-5192]]). * Use '''' (see [[springdocs>beans.html#beans-factory-scopes-other-injection|Scoped beans as dependencies]]). Drawback: does not make sense with ''prototype'' scope as each method invocation will operate with new bean instance which makes it impossible to use this approach with stateful beans. See also: * [[https://prasanthnath.wordpress.com/2013/03/21/injecting-a-prototype-bean-into-a-singleton-bean/|Injecting a prototype bean into a singleton bean]] === How to configure Spring application to use aspect [[springdocs>aop.html#aop-aj-ltw|load-time weaving (LTW)]]? === - Create ''META-INF/aop.xml'' (should be included into WAR or JAR) and e.g. aspect ''MultiThreadedHttpConnectionManagerAdvice.java'' as below: package aspects; import java.lang.reflect.Field; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.Map; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.util.ReflectionUtils; @Aspect public class MultiThreadedHttpConnectionManagerAdvice { private final Map infos = new IdentityHashMap(); /** * Statistics is reset after this amount of invocations. */ private static final int NUMBER_OF_INVOCATIONS_PER_MEASUREMENT = 100; static class ConnectionPoolInfo { public int invocationsNumber; public int maxWaitingThreadsNumber; @Override public String toString() { return "maxWaitingThreadsNumber:" + maxWaitingThreadsNumber; } } /** * @param pool instance of {@link MultiThreadedHttpConnectionManager$ConnectionPool}. */ private void updateStatistics(Object pool) { LinkedList waitingThreads = getField(pool, "waitingThreads"); synchronized (infos) { ConnectionPoolInfo info = infos.get(pool); if (info == null) { info = new ConnectionPoolInfo(); infos.put(pool, info); } if (info.maxWaitingThreadsNumber < waitingThreads.size()) { info.maxWaitingThreadsNumber = waitingThreads.size(); } if (++info.invocationsNumber % NUMBER_OF_INVOCATIONS_PER_MEASUREMENT == 0) { LogFactory.getLog(getClass()).error( "After " + info.invocationsNumber + " invocations " + info + " poolsNumber:" + infos.size()); info.maxWaitingThreadsNumber = 0; } } } @After("execution(* org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.ConnectionPool.createConnection*(..))") public void createConnection(JoinPoint pjp) { updateStatistics(pjp.getThis()); } @Before("execution(* org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.ConnectionPool.getFreeConnection*(..))") public void getFreeConnection(JoinPoint pjp) { updateStatistics(pjp.getThis()); } /** * Get the value of private field {@code fieldName} of given object {@code target}. */ @SuppressWarnings("unchecked") public static T getField(Object target, String fieldName) { Field field = ReflectionUtils.findField(target.getClass(), fieldName); ReflectionUtils.makeAccessible(field); return (T) ReflectionUtils.getField(field, target); } } - Configure Spring to use LWP: - Add LWP agent to JVM startup: ''%%java ... -javaagent:/path/to/org.springframework.instrument-3.1.1.RELEASE.jar ...%%'' **Troubleshooting:** If during application startup ''[[http://forum.spring.io/forum/spring-projects/aop/72229-nosuchmethoderror-aspect-aspectof#post469223|java.lang.NoSuchMethodError: aspectOf()]]'' is thrown then make sure that aspects are listed in ''aop.xml''. See also [[springdocs>aop.html#aop-aj-configure|Configuring AspectJ aspects using Spring IoC]]. === How to make a bean to implement an additional interface? === Sounds weird, but let's assume you have a class that you don't have sources for and you want to make this class implement an additional interface, ''[[springjava>core/PriorityOrdered.html|PriorityOrdered]]'' for example and to route all calls to this interface to some bean, while all other calls should be routed to original bean. The solution is based on JDK proxy mechanism, it means that a bean that needs to be tricked (''service.MyServiceImpl'' in the example), has to implement some business interface (''service.MyService'') which is exposed to other beans in the context. First write the implementation for the interface that needs to be introduced (if necessary): package util; import org.springframework.core.PriorityOrdered; /** * Simple implementation of {@link PriorityOrdered} interface that returns the pre-configured order. */ public class FixedPriorityOrder implements PriorityOrdered { private final int order; private FixedPriorityOrder(int order) { this.order = order; } @Override public int getOrder() { return order; } } Now bringing all together: org.springframework.beans.factory.InitializingBean org.springframework.core.PriorityOrdered service.MyService ... normalPriorityOrder ==== JDBC ==== === How ''@Transactional'' support is actually implemented? === * Annotation-driven transaction support is enabled by adding '''' in your context file. Element is processed by ''[[springfish>org.springframework.transaction/src/main/java/org/springframework/transaction/config/AnnotationDrivenBeanDefinitionParser.java?r=2462|AnnotationDrivenBeanDefinitionParser]]'' * The last one adds ''[[springfish>org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/BeanFactoryTransactionAttributeSourceAdvisor.java?r=154|BeanFactoryTransactionAttributeSourceAdvisor]]'' into the context, which implements ''[[springfish>org.springframework.aop/src/main/java/org/springframework/aop/PointcutAdvisor.java?r=114|PointcutAdvisor]]'' interface. * ''[[springfish>org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/BeanFactoryTransactionAttributeSourceAdvisor.java?r=154#l63|getPointcut()]]'' of this advisor returns pre-configured ''[[springfish>org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSourcePointcut.java?r=154|TransactionAttributeSourcePointcut]]'' class instance, that uses ''[[springfish>org.springframework.transaction/src/main/java/org/springframework/transaction/annotation/AnnotationTransactionAttributeSource.java?r=3566|AnnotationTransactionAttributeSource]]'' to check if the given method / class should be advised in ''[[springfish>org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSourcePointcut.java?r=154#l34|boolean matches(Method method, Class targetClass)]]'' method. * ''[[springfish>org.springframework.aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java?r=150#l73|getAdvice()]]'' method of this class returns pre-configured ''[[springfish>org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java?r=3465|TransactionInterceptor]]'' class instance (which is pre-configured with transaction manager). * For each created bean ''[[springfish>org.springframework.aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java?r=596#l346|AbstractAutoProxyCreator#wrapIfNecessary(Object bean, String beanName, Object cacheKey)]]'' (which implements ''[[https://fisheye.springsource.org/browse/spring-framework/trunk/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanPostProcessor.java?r=596|BeanPostProcessor]]'') is called. It looks up advisors in the context and finally calls ''[[springfish>org.springframework.aop/src/main/java/org/springframework/aop/support/AopUtils.java?r=3570#l270|AopUtils#findAdvisorsThatCanApply(List candidateAdvisors, Class clazz)]]''. If the last one returns the non-empty list, then the target bean is warped into a proxy. * When the target class instance method is invoked, the ''[[springfish>org.springframework.aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java?r=3137#l148|JdkDynamicAopProxy#invoke(Object proxy, Method method, Object[] args)]]'' is called. It uses ''[[springfish>org.springframework.aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java?r=305#l48|DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class targetClass)]]'' to collect all advisors and build the advisor chain. * ''[[springfish>org.springframework.transaction/src/main/java/org/springframework/transaction/interceptor/TransactionInterceptor.java?r=3465#l91|TransactionInterceptor#invoke(final MethodInvocation invocation)]]'' is called, which creates new transaction using the transaction manager. === What are the possibilities to add transactional support to my application? === * Using programmatic transaction management facilities via ''[[springjava>transaction/support/TransactionTemplate.html|TransactionTemplate]]'' (see [[springdocs>transaction.html#tx-prog-template|Using the TransactionTemplate]]) or via ''[[springjava>transaction/PlatformTransactionManager.html|PlatformTransactionManager]]'': @Autowired private PlatformTransactionManager transactionManager; void doInTransaction() { DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition(); transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition); // Do the job transactionManager.commit(transactionStatus); // Error handling is omitted: //transactionManager.rollback(transactionStatus); } * Using ''[[springjava>transaction/annotation/Transactional.html|@Transactional]]'' annotations and '''' (see [[springdocs>transaction.html#transaction-declarative-annotations|Using @Transactional]]) * Using Spring 2.0 transaction advice via '''' and '''' (see [[springdocs>transaction.html#transaction-declarative-txadvice-settings| settings]]) And [[http://forum.springsource.org/showthread.php?t=37178#post112485|more exotic]]: * Using Spring 1.x ''[[springjava>transaction/interceptor/TransactionProxyFactoryBean.html|TransactionProxyFactoryBean]]'' * Using ''[[springjava>aop/framework/ProxyFactoryBean.html|ProxyFactoryBean]]'' and ''[[springjava>transaction/interceptor/TransactionInterceptor.html|TransactionInterceptor]]'' * Using ''[[springjava>aop/framework/autoproxy/BeanNameAutoProxyCreator.html|BeanNameAutoProxyCreator]]'' or a different ''[[springjava>aop/framework/autoproxy/AbstractAutoProxyCreator.html|...AutoProxyCreator]]'' * Using EJB transaction management facilities === How to define custom mapping for SQL error codes to exception classes? === In this scenario we want custom ''DataTruncationException'' to be thrown in case of specific SQL error codes: package org.mycompany.dao.exception; import org.springframework.dao.DataIntegrityViolationException; public class DataTruncationException extends DataIntegrityViolationException { public DataTruncationException(String msg, Throwable cause) { super(msg, cause); } } 8152 -3401 Later in your Web controller you can for example map given exception to corresponding HTTP code: import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; ... @ExceptionHandler(DataTruncationException.class) @ResponseStatus(value = HttpStatus.REQUEST_ENTITY_TOO_LARGE) @ResponseBody public String handleDataTruncationException(DataTruncationException ex) { return ExceptionUtils.getStackTrace(ex); } === How to mock JNDI lookups in unit tests? === From [[stackoverflow>3039725|here]] and [[stackoverflow>5682732|here]]: * In your shared Spring context define: * In your test context define: * In your unit test: @RunWith(SpringRunner.class) @ContextConfiguration({"classpath:/com/company/module/dao/dao-test.xml"}) public class MyDaoImplTest { @Resource private JdbcTemplate myDao; @Resource private DataSource testDataSource; @Before public void setupJndi() throws NamingException { SimpleNamingContextBuilder.emptyActivatedContextBuilder(); SimpleNamingContextBuilder.getCurrentContextBuilder().bind("java:comp/env/jdbc/SampleDB", testDataSource); } } === Why do I get ''CacheException'' in my unit test? === Setup: * Hibenate session is managed by Spring and has second-level cache enabled: /.../hibernate-mapping.xml ... true true net.sf.ehcache.hibernate.EhCacheProvider net.sf.ehcache.hibernate.EhCacheRegionFactory /.../ehcache.xml * Unit test classes refer this context using ''@ContextConfiguration'': import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @ContextConfiguration("classpath:/.../dao-context.xml") @RunWith(SpringRunner.class) public class MyTest { ... } * The exception is: Caused by: net.sf.ehcache.CacheException: Another CacheManager with same name 'hibernate-cache' already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following: 1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary 2. Shutdown the earlier cacheManager before creating new one with same name. The source of the existing CacheManager is: URLConfigurationSource [url=file:/C:/workspace/chepo/chepo-dataaccess-db/bin/org/epo/lifesciences/chepo/dao/ehcache.xml] at net.sf.ehcache.CacheManager.assertNoCacheManagerExistsWithSameName(CacheManager.java:457) at net.sf.ehcache.CacheManager.init(CacheManager.java:354) at net.sf.ehcache.CacheManager.(CacheManager.java:242) at net.sf.ehcache.hibernate.EhCacheRegionFactory.start(EhCacheRegionFactory.java:79) ... 59 more at net.sf.ehcache.hibernate.EhCacheRegionFactory.start(EhCacheRegionFactory.java:89) at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:238) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872) at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:860) at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:779) at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:188) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452) ... 52 more at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:730) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:196) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:103) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1) at org.springframework.test.context.support.DelegatingSmartContextLoader.loadContext(DelegatingSmartContextLoader.java:228) at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124) at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148) ... 24 more In this scenario ''net.sf.ehcache.CacheManager'' with name ''hibernate-cache'' is started by some unit test and as ''CacheManager'' shares cache instances via static variable, this cache instance looks like already being initialized for following unit test. In order to solve the issue make sure that: * You close the context after each unit test (see [[stackoverflowa>7498384/267197|''SpringRunner'' does not close the Application Context at the end of JUnit test case]]): import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @ContextConfiguration("classpath:/.../dao-context.xml") @RunWith(SpringRunner.class) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) public class MyTest { ... } This will guarantee that Hibernate factory is correctly closed, which in its turn will shutdown cache manager. * You shutdown all other (not Hibernate managed) cache managers using ''destroy-method'': ==== MVC ==== === [[stackoverflow>6395621|How to call a static method in JSP/EL?]] === In general you need to make you method as normal getter, however with [[springdocs>expressions.html#expressions-types|SpEL]] this is possible: <%@taglib prefix="s" uri="http://www.springframework.org/tags" %> Other examples of SpEL expressions: * ''%%#{T(Math).min(T(Runtime).getRuntime().maxMemory() / 3, 0L + T(Integer).MAX_VALUE)}%%'' \\ Mind how 2nd argument was forced to ''long'', otherwise "Method call of 'min' is ambiguous, supported type conversions allow multiple variants to match" exception will be thrown. * ''%%#{T(org.apache.commons.io.IOUtils).toString(new java.net.URL('file:///etc/HOSTNAME'))}%%'' \\ This will stringify the given system file. For non-JSP, non-EL approach check [[stackoverflowa>39374686/267197|Invoke static method from spring config]]: anArgument === What is the lifecycle of Spring MVC Controller? === By default Spring MVC controllers are singletons. The request lifecycle is displayed on this diagram: {{http://farm1.staticflickr.com/14/89101625_26c5be9fd9_b.jpg?800|Spring MVC Request Lifecycle}} === What are the options for exception handling in Spring MVC? === From [[http://doanduyhai.wordpress.com/2012/05/06/spring-mvc-part-v-exception-handling/|Spring MVC part V: Exception handling]]: * Return the error code with exception as a body: import org.apache.commons.lang.exception.ExceptionUtils; @ExceptionHandler(IllegalArgumentException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseBody public String handleIllegalArgumentException(IllegalArgumentException ex) { return ExceptionUtils.getStackTrace(ex); } If you specify the custom reason for error response, then response body is ignored: @ExceptionHandler(IllegalArgumentException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "The passed argument is invalid") public void handleIllegalArgumentException(IllegalArgumentException ex) { } * Redirect to a dedicated error page: @ExceptionHandler(IndexOutOfBoundsException.class) public String handleException(IndexOutOfBoundsException ex) { return "pages/errorPage"; } === How to override returned media type for response body in case of exception handling? === It may happen that during processing of request the exception occurs. If mapped method defines the produced media type, this media type is remembered and used for response body of the exception handler, which in most cases is not appropriate, because the browser expects text/plain MIME type for error HTTP codes. In below scenario let's assume that ''myService.getInfo()'' throws ''IllegalArgumentException''. In this case Spring will convert returned value from ''handleIllegalArgumentException()'' into application/json, however more appropriate will be text/plain. To overcome this, exception hander should reset the remembered produced types before returning the response body: import java.security.Principal; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.HandlerMapping; ... @RequestMapping(value = "/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public Info getInfo(Principal principal) { return myService.getInfo(principal); } @ExceptionHandler(IllegalArgumentException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseBody public String handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException ex) { resetProducibleMediaTypes(request); return ExceptionUtils.getStackTrace(ex); } /** * Media types to negotiate for error conditions. */ private static final Set DEFAULT_MEDIA_TYPES; static { Set mediaTypes = new HashSet(); Collections.addAll(mediaTypes, MediaType.TEXT_PLAIN, MediaType.ALL); DEFAULT_MEDIA_TYPES = Collections.unmodifiableSet(mediaTypes); } /** * Content types, which have been remembered to handle the return type of handler method, need to be reset to allow * the return type of exception handler to have different content type. */ private static void resetProducibleMediaTypes(HttpServletRequest request) { request.setAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, DEFAULT_MEDIA_TYPES); } === How to add HTTP request post-processing handler in Spring Web? === Add the following to your Web context (see also [[stackoverflowa>3469996/267197|Bind Spring ''HandlerInterceptor'' only to one controller]]): === [[stackoverflowa>21400914/267197|How to use a request-scoped bean inside a spawned thread?]] === Spring infrastructure (e.g. ''[[springjava>web/servlet/DispatcherServlet.html|DispatcherServlet]]'') binds the HTTP request to current thread, which makes it inaccessible for spawned threads. To overcome the problem the following conditions should be met: * ''threadContextInheritable'' property of ''[[springjava>web/servlet/DispatcherServlet.html|DispatcherServlet]]'' or ''[[springjava>web/filter/RequestContextFilter.html|RequestContextFilter]]'' should be set to true, for example: org.springframework.web.servlet.DispatcherServlet threadContextInheritable true * The execution that does not pool threads (e.g. ''[[springjava>core/task/SimpleAsyncTaskExecutor.html|SimpleAsyncTaskExecutor]]'') should be used. That means: ''[[springjava>scheduling/concurrent/ThreadPoolTaskExecutor.html|ThreadPoolTaskExecutor]]'' or ''[[springjava>jca/work/WorkManagerTaskExecutor.html|WorkManagerTaskExecutor]]'' will not work. === Is it possible to return several model objects? === One should use ''[[springjava>web/servlet/ModelAndView.html|ModelAndView]]'' for that (from [[stackoverflowa>5068941/267197|Can I return two models in ModelAndView in Spring MVC]]): @RequestMapping(value = {"/registrationForm"}, method = RequestMethod.GET) public ModelAndView newForm() { final ModelAndView modelAndView = new ModelAndView("registrationForm"); modelAndView.addObject("registrationForm", new RegistrationForm()); modelAndView.addObject("sharedData", sharedData); return modelAndView; } === How to enable validation in Spring MVC? === As from [[springdocs>validation.html#validation-beanvalidation|Spring Validation]] and [[stackoverflow>5576790#5577093|Validation in Spring MVC]] you need to: * Include ''javax.validation:validation-api'' into you dependencies list. * Annotate the model with ''@Valid'': @RequestMapping(method = RequestMethod.POST) public String onFormSubmitted(@ModelAttribute("myForm") @Valid MyForm myForm, BindingResult result) { if (result.hasErrors()) { return "myForm"; } return "formSubmitted"; } * Initialize validator for binder: @InitBinder protected void initBinder(WebDataBinder binder) { binder.setValidator(new MyFormValidator()); } * Implement custom validator: import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; public class MyFormValidator implements Validator { /** * @see org.springframework.validation.Validator#supports(java.lang.Class) */ public boolean supports(Class clazz) { return MyForm.class.isAssignableFrom(clazz); } /** * @see org.springframework.validation.Validator#validate(java.lang.Object, org.springframework.validation.Errors) */ public void validate(Object target, Errors errors) { ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "required", "Field is required."); } } Additional info: * [[http://blog.jteam.nl/2009/08/04/bean-validation-integrating-jsr-303-with-spring/|Bean validation: integrating JSR-303 with Spring]] * [[http://numberformat.wordpress.com/tag/spring-mvc-validation/|Spring MVC Validation using Commons Validator]] === How to integrate [[googlecode>apis/recaptcha/docs/java.html|ReCaptcha]] into Spring MVC? === See [[http://wheelersoftware.com/articles/recaptcha-java-2.html|here]] and [[stackoverflow>4991335|here]]: net.tanesha.recaptcha4j recaptcha4j 0.0.7 Use of a "*" for the field name is important, otherwise MVC will try to resolve the field value: <%@ page import="net.tanesha.recaptcha.ReCaptcha" %> <%@ page import="net.tanesha.recaptcha.ReCaptchaFactory" %> <%@ page import="com.company.module.web.SharedData" %> ...
<% SharedData sharedData = (SharedData) request.getAttribute("sharedData"); ReCaptcha captcha = ReCaptchaFactory.newReCaptcha(sharedData.getRecaptchaPublicKey(), sharedData.getRecaptchaPrivateKey(), false); out.print(captcha.createRecaptchaHtml(null, null)); %>
The controller code: import net.tanesha.recaptcha.ReCaptchaImpl; import net.tanesha.recaptcha.ReCaptchaResponse; @Controller @SessionAttributes("sharedData") public class RegistrationFormController { @Autowired private SharedData sharedData; @RequestMapping(value = {"/registrationForm"}, method = RequestMethod.GET) public ModelAndView newForm() { final ModelAndView modelAndView = new ModelAndView("registrationForm"); modelAndView.addObject("registrationForm", new RegistrationForm()); modelAndView.addObject("sharedData", sharedData); return modelAndView; } @RequestMapping(value = {"/registrationForm"}, method = RequestMethod.POST) public String onRegistrationFormSubmitted(HttpServletRequest request, @RequestParam("recaptcha_challenge_field") String challenge, @RequestParam("recaptcha_response_field") String response, @ModelAttribute("registrationForm") @Valid RegistrationForm registrationForm, BindingResult result) { final ReCaptchaImpl recaptcha = new ReCaptchaImpl(); recaptcha.setPrivateKey(sharedData.getRecaptchaPrivateKey()); final ReCaptchaResponse captchaResponse = recaptcha.checkAnswer(request.getRemoteAddr(), challenge, response); if (!captchaResponse.isValid()) { // 2nd argument should match in JSP: result.addError(new FieldError("comment", "captcha", "Captcha is wrong")); } if (result.hasErrors()) { return "registrationForm"; } // Perform business logic, e.g. persist registration data return "formSubmitted"; } } And shared data is initialized in Spring context: Check also [[stackoverflow>217511|Integrating Captcha with Spring Security]], [[stackoverflow>3326778|CAPTCHA in java]].
=== How do you make the "welcome file" (''index.html'') be handled via Spring and not your web container? === From [[http://technologicaloddity.com/2010/03/25/spring-welcome-file-without-redirect/|here]]: spring *.html /index.html index.html To map certain resources back to default servlet, use the following: default *.jpg :WARN: Mind the priority of URL-matching rules((See [[stackoverflowa>45360749/267197|Difference between / and /* in servlet mapping url pattern]])): - URL patterns (''/*'') - extension patterns (''*.html'') - default servlet (''/'') {{tag>Spring Jersey JSON MVC}}