Initially I had a converter for almost every entity classes in my project. I decided to drop all those converters and created a ConverterFactory that will do all the conversion.
#Service("dataConverterFactory")
public class DataConverterFactory implements ConverterFactory<String, CommonEntity> {
private static final Logger logger = LoggerFactory.getLogger(DataConverterFactory.class);
private final EntityManager em;
#Autowired
public DataConverterFactory(EntityManager em) {
this.em = em;
}
#Override
public <T extends CommonEntity> Converter<String, T> getConverter(Class<T> targetType) {
return new GenericConverter(targetType);
}
private final class GenericConverter<T extends CommonEntity> implements Converter<String, T> {
private final Class<T> targetType;
public GenericConverter(Class<T> targetType) {
this.targetType = targetType;
}
#Override
public T convert(String text) {
if (text != null && !text.isEmpty() && !text.equalsIgnoreCase("-1")) {//Ignore - to be determined later
try {
return (T) em.find(targetType, Integer.valueOf(text.trim()));
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
return null;
}
}
}
This works fine but my tests are failing when conversion is required. Here is a snippet from my test xml configuration file
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="dataConverterFactory"/>
</set>
</property>
</bean>
<bean id="dataConverterFactory" class="com.xyz.converter.DataConverterFactory"/>
Sample test
mockMvc.perform(fileUpload("/complaint/new")
.file(file)
.param("customer", "1")
)
In the above test,I am trying to submit a Complaint but it will fail because customer will be null.
Parameter customer should be converted to class Customer which extends CommonEntity.
public class Complaint extends CommonEntity {
#NotNull
#ManyToOne
#JoinColumn(name = "customer_id", updatable = false)
private Customer customer;
}
How do mock/set conversion service to work in my tests?
private MockMvc mockMvc;
#Before
public void setUp() {
YourController converterController = new YourController();
FormattingConversionService cs = new FormattingConversionService();
cs.addConverter(new YourConverter());/////here
mockMvc = MockMvcBuilders.standaloneSetup(converterController)
.setConversionService(cs)/////here
.build();
}
Related
My objectmapper not working when I use spring controller & class for requestbody inheritation .
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type", visible = true)
#JsonSubTypes({
#JsonSubTypes.Type(value = RecipeVersion.class, name = "recipe"),
#JsonSubTypes.Type(value = DietVersion.class, name = "diet"),
})
public interface DocumentVersion {
Info getInfo();
void setInfo(Info info);
}
and also
#Data
public class DietVersion implements DocumentVersion {
private LocalizedText warnings;
private List<DietDay> days = new LinkedList<>();
private Info info = new Info();
private String getType() {
return "diet";
}
}
Ok. I have BaseController for diets and recipes
abstract public class BaseController<T extends Document<V>, V extends DocumentVersion> {
abstract protected BaseService<T, V> getService();
#PostMapping("/{docId}/version/last")
#ResponseStatus(HttpStatus.NO_CONTENT)
public void saveVersion(#PathVariable("docId") String docId, #RequestBody V version, Authentication authentication) {
getService().replaceLastVersion(docId, version, authentication);
}
}
and some realizations. example for diet
#Controller
#RequestMapping("/diet")
public class DietController extends BaseController<Diet, DietVersion> {
private final DietService dietService;
#Autowired
public DietController(DietService dietService) {
this.dietService = dietService;
}
#Override
protected DietService getService() {
return dietService;
}
#Override
public void saveVersion(String docId, DietVersion version, Authentication authentication) {
super.saveVersion(docId, version, authentication);
}
}
But when I send json with info, days, type ('diet') to '/diet/1/version/last' then I see in debug mode that my DietVersion pure clear and has no any data. Why ?
How to change settings for objectmapper ?
what if you provide all this in your DietController class.
public void saveVersion(#PathVariable("docId") String docId, #RequestBody V version, Authentication authentication){
I am trying to set up an integration testing with mockMvc, and i have a problem with it. Indeed, spring doesn't integrate any validation annotation.
For more precision, i put the code of the Controller class which could be tested :
#Controller
public class UserRegisterController {
private final Log log = LogFactory.getLog(UserRegisterController.class);
private UserManager userManager;
#Autowired
public UserRegisterController(UserManager userManager){
this.userManager = userManager;
}
/**
* Register a new user.
*
*/
#RequestMapping(value = "/User/Register",
method = RequestMethod.GET
)
public #ResponseBody SimpleMessage submitForm(
#Valid UserInfoNew userInfo,
BindingResult result
){
if(log.isInfoEnabled())
log.info("Execute UserRegister action");
SimpleMessage message;
try {
if(result.hasErrors()){
if(log.isFatalEnabled())
log.fatal("Parameters sent by user for registering aren't conform. Errors are : "
+ result.getFieldErrors().toString());
throw new Exception(result.getFieldErrors().toString());
}
User user = new User();
user.setLogin(userInfo.getLogin());
user.setFamilyName(userInfo.getFamilyName());
user.setFirstName(userInfo.getFirstName());
user.setPassword(userInfo.getPassword());
user.setDateBirthday(userInfo.getDateBirthday());
user.setEmail(userInfo.getEmail());
user.setMobile(userInfo.getMobile());
user.setAddress(userInfo.getAddress());
userManager.createUser(user);
User newUser = userManager.findLastUserCreated();
//Change to null some sensitive or useless return parameters
newUser.setPassword(null);
//
message = new SimpleMessage(null, newUser);
} catch (Exception e) {
if(log.isErrorEnabled())
log.error("A problem of type : " + e.getClass()
+ " has occured, with message : " + e.getMessage());
message = new SimpleMessage(
new SimpleException(e.getClass(), e.getMessage()), null);
}
return message;
}
}
Then, the object with contain both hibernate and javax annotation for validation :
public abstract class UserParameters {
#Min(1)
protected Long id;
#Length(min=4, max=20)
protected String login;
#Length(min=4, max=20)
protected String familyName;
#Length(min=4, max=20)
protected String firstName;
#Pattern(regexp="^.*(?=.{8,20})(?=.*[a-z]+)(?=.*[a-z]+)(?=.*[A-Z]+)(?=.*[A-Z]+)"
+ "(?=.*[0-9]+)(?=.*[0-9]+)(?=.*[#$%*#]+).*$")
protected String password;
#Past
protected Calendar dateBirthday;
#Email
#Length(max=255)
protected String email;
#Pattern(regexp="^[0]{1}[67]{1}[ .-]{1}[0-9]{2}[ .-]{1}"
+ "[0-9]{2}[ .-]{1}[0-9]{2}[ .-]{1}[0-9]{2}$")
protected String mobile;
#Length(max=255)
protected String address;
protected Calendar dateCreation;
protected Calendar dateLastAccess;
}
public class UserInfoNew extends UserParameters implements Serializable{
private static final long serialVersionUID = 4427131414801253777L;
#NotBlank
public String getLogin() {
return login;
}
public void setLogin(String Login) {
this.login = Login;
}
public String getFamilyName() {
return familyName;
}
public void setFamilyName(String Name) {
this.familyName = Name;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String FirstName) {
this.firstName = FirstName;
}
#NotBlank
public String getPassword() {
return password;
}
public void setPassword(String Password){
this.password = Password;
}
public Calendar getDateBirthday() {
return dateBirthday;
}
public void setDateBirthday(Calendar strBirthDay) {
this.dateBirthday = strBirthDay;
}
public String getEmail() {
return email;
}
public void setEmail(String Mail) {
this.email = Mail;
}
#NotBlank
public String getMobile() {
return mobile;
}
public void setMobile(String Mobile) {
this.mobile = Mobile;
}
public String getAddress() {
return address;
}
public void setAddress(String Address) {
this.address = Address;
}
}
and the class which realizes the test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = {
WebInit_Test.class,
AppConfig_Test.class,
WebConfig_1.class,
WebConfig_2.class,
WebSocketConfig.class
})
public class UserControllersTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.alwaysExpect(status().isOk())
.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
.build();
}
#Test
public void userRegister() throws Exception {
//doesn't consider #Valid during test
mockMvc.perform(get("/User/Register?Login=A&Password=aaAA&Mobile=0134320285")
.contentType(MediaType.ALL)
)
.andExpect(jsonPath("error").exists());
}
}
When i launch the test, the error item doesn't exist, whereas login, password and mobile can't be validate by javax and hibernate annotation. Moreover, when i try to send an URL to localhost, validation worked and new user is not saved in database.
As you can see, i use a java code configuration for my web layer, i suppose the problem come from it. Moreover i download a project from the spring team in github (link : github.com/spring-projects/spring-mvc-showcase) which details all kind of test we can do with mockmvc. The validation one (in "org.springframework.samples.mvc.validation" package) doesn't work with my project configuration but very well with in it's original config.
To finish, i send you all my configuration classes
#Configuration
public class WebInit_Test extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { AppConfig_Test.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig_1.class, WebConfig_2.class, WebSocketConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected void customizeRegistration(Dynamic registration) {
registration.setInitParameter("dispatchOptionsRequest", "true");
registration.setLoadOnStartup(1);
}
}
#Configuration
#ImportResource({
"classpath:/applicationContext-dao.xml",
"classpath:/applicationContext-datasource-test.xml",
"classpath:/applicationContext-service.xml"
})
public class AppConfig_Test {
}
#Configuration
#EnableWebMvc
#ComponentScan(
basePackages = "project.web",
excludeFilters = #ComponentScan.Filter(type= FilterType.ANNOTATION, value = Configuration.class)
)
public class WebConfig_1 extends WebMvcConfigurationSupport {
#Autowired
private FormattingConversionServiceFactoryBean conversionService;
#Bean
#Override
public FormattingConversionService mvcConversionService() {
FormattingConversionService conversionService = this.conversionService.getObject();
addFormatters(conversionService);
return conversionService;
}
}
#Configuration
public class WebConfig_2 extends WebMvcConfigurerAdapter{
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/**
* Configure output mapping, see
* {#link http://stackoverflow.com/questions/4823358/spring-configure-responsebody-json-format}
* for more information
*
* #param converters
* a list of {#link HttpMessageConverter<?>}
*/
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
converter.setObjectMapper(objectMapper);
converters.add(converter);
super.configureMessageConverters(converters);
}
}
#Configuration
//#EnableScheduling
#ComponentScan(
basePackages="project.web",
excludeFilters = #ComponentScan.Filter(type= FilterType.ANNOTATION, value = Configuration.class)
)
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/friendship", "/message", "/journey", "/information");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/client").withSockJS();
}
}
Thanks for your help.
I got same problem, after updating validator to 5.1.0.Final. Application is working perfectly but REST tests not (#Valid annotation is not considered at all). I resolved the problem with adding one additional dependency only for tests:
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
<scope>test</scope>
</dependency>
If you are using hibernate version 5.4.1.Final just add below dependency for your test
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.0</version>
</dependency>
This dependency is "provided" dependency. You can find matching dependency for you version of hibernate at maven site. https://mvnrepository.com/artifact/org.hibernate/hibernate-validator/5.4.1.Final
For example if you are using 5.4.1 version go to above link and check for provided dependencies and use it.
Ok, I have just found what is responsible for the fact that validation was not consider by mockmvc. It's just a bad dependency in my pom:
I used
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
and when i replaced it by
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.1.0.Final</version>
</dependency>
the test work just fine!
the problem is solved, but I am wondering why the problem comes from this part. hibernate-validator 5.1.0.Final include javax validation in transitive dependency, so normally, this problem would never have appeared.
I just added
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
and it worked
This is the Application.java class
#Configuration
#SpringBootApplication
//#EnableGlobalMethodSecurity(prePostEnabled=true)
#EnableTransactionManagement
#EnableCaching
#EnableJpaRepositories(basePackages="om.gov.moh.irs.dao.repos",repositoryImplementationPostfix="CustomImpl")
public class Application extends SpringBootServletInitializer {
#Autowired
Environment env;
#Bean
#ConfigurationProperties("spring.datasource")
public ComboPooledDataSource dataSource() {
return new ComboPooledDataSource();
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasenames("messages"); // name of the resource bundle
source.setUseCodeAsDefaultMessage(true);
return source;
}
#Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(env.getProperty("multipart.maxFileSize"));
factory.setMaxRequestSize(env.getProperty("multipart.maxRequestSize"));
return factory.createMultipartConfig();
}
}
This is the dto class which Im trying to cache.
public class PaginationDto implements Serializable {
private static final long serialVersionUID = 1L;
public Integer totalRecords;
public List<?> paginatedList;
//constructor and getter setter here
}
Controller class
#RequestMapping(value="categories", method=RequestMethod.POST, produces = { "application/json" })
public #ResponseBody ResultDecorator searchCategories(#RequestBody CategoryDto categoryDto) int pageSize, #RequestParam("sort") String sortOrder) throws BusinessException{
return handler.resolveResult(categoryService.searchCategory(categoryDto), OperationEnum.SEARCH);
}
Service class where #cacheable is defined.
#Override
#Cacheable("category")
public PaginationDto searchCategory(CategoryDto categoryDto) throws CategoryException {
System.out.println("#######category");
PaginationDto paginationDtoResponse = null;
paginationDtoResponse = categoryRepoCustom.fetchCategories(categoryDto);
return paginationDtoResponse;
}
Defined ehcache.xml file
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
updateCheck="true" monitoring="autodetect" dynamicConfig="true">
<cache name="category"
maxElementsInMemory="100" eternal="false"
overflowToDisk="false"
timeToLiveSeconds="30000" timeToIdleSeconds="0"
memoryStoreEvictionPolicy="LFU" transactionalMode="off">
</cache>
</ehcache>
On hitting this http://localhost:9190/isa/categories each time console log is printing, which means data is not fetching from the cache.
Depending on project's requirement, we want to change from JdbcTemplate into NamedParameterJdbcTemplate in database handling. But I got null point error when I test my coding. I'm sure the error has occurred because of my configuration, but I could not resolve it.
Config
#Value("${spring.datasource.driver-class-name}")
protected String driver;
#Value("${spring.datasource.url}")
protected String url;
#Value("${spring.datasource.username}")
protected String user;
#Value("${spring.datasource.password}")
protected String pass;
#Bean
public DataSource dataSource(){
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(pass);
dataSource.setTestOnBorrow(true);
dataSource.setTestOnReturn(true);
dataSource.setTestWhileIdle(true);
return dataSource;
}
#Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
#Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(){
return new NamedParameterJdbcTemplate(dataSource);
}
DaoImpl
#Repository
public class DAOImpl implements DAO {
#Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
#Override
public List<Type> findAllType() {
String sql = "select * from type";
RowMapper<Type> rm = new TypeRowMapper();
List<Type> list = jdbcTemplate.query(sql, rm);
return list;
}
}
Test
public class Test {
#Autowired
protected DAOImpl dao;
public static void main(String[] args) {
List<Type> list = new ArrayList<Type>();
Test test = new Test();
System.out.println(test.dao);
list = test.dao.findAllType();
for(Type type : list){
System.out.println(type.getName());
}
}
}
Stacktrace
null
Exception in thread "main" java.lang.NullPointerException
at com.example.dao.Test.main(Test.java:18)
I create form and controller this form have some validation constrains using Hibernate validator. I face problem when starting test the validation constrains but I got Blue Exception page with the attributemodel with the rejected.
This the configuration
#Configuration
#ComponentScan(basePackages = {"com.whatever.core.web"})
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurationSupport {
private static final String MESSAGE_SOURCE = "/WEB-INF/classes/messages";
private static final String TILES = "/WEB-INF/tiles/tiles.xml";
private static final String VIEWS = "/WEB-INF/views/**/views.xml";
private static final String RESOURCES_HANDLER = "/resources/";
private static final String RESOURCES_LOCATION = RESOURCES_HANDLER + "**";
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping requestMappingHandlerMapping = super.requestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
requestMappingHandlerMapping.setUseTrailingSlashMatch(false);
return requestMappingHandlerMapping;
}
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJacksonHttpMessageConverter());
}
#Bean(name = "messageSource")
public MessageSource configureMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename(MESSAGE_SOURCE);
messageSource.setCacheSeconds(5);
return messageSource;
}
#Bean
public TilesViewResolver configureTilesViewResolver() {
return new TilesViewResolver();
}
#Bean
public TilesConfigurer configureTilesConfigurer() {
TilesConfigurer configurer = new TilesConfigurer();
configurer.setDefinitions(new String[] {TILES, VIEWS});
return configurer;
}
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(configureMessageSource());
return validator;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(RESOURCES_HANDLER).addResourceLocations(RESOURCES_LOCATION);
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
and the controller here
if(result.hasErrors()){
return null; OR "view name"
}
User user = new User();
user.setUsername(userModel.getUsername());
user.setFirstName(userModel.getFirstName());
user.setLastName(userModel.getLastName());
user.setGender(userModel.getGender());
user.setLocation(userModel.getLocation());
user.setPassword(passwordEncoder.encodePassword(userModel.getPassword(),null));
userRepository.save(user);
doAutoLogin(userModel.getUsername(),userModel.getPassword(),request);
return "redirect:/home";
NOTE: I use springMVC, spring security, tiles, and hibernate validator
I used SpringMVC with hibernate validator with XML configuration and portal environment and work fine I don't know what the wrong here!!
I Found the issue! the signature of the method controller should be like this
public String signup(#ModelAttribute("userModel") #Valid SignupForm userModel,BindingResult result,HttpServletRequest request,HttpServletResponse response,ModelMap model)
as what I read in sprinsource forum, the BindingResult should follow the modelAttribute and work find. I didn't find any official documentation for this but its work now.
to see the thread of springsource forum check this link http://forum.springsource.org/showthread.php?85815-BindException-Thrown-on-bind-errors-(instead-of-returning-errors-to-controller-method