I use Spring version 3.2.2.RELEASE.
I want to create a webapp with hibernate and have the folllowing configuration:
public class LifepulseServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { PersistenceConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { LifepulseWebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected WebApplicationContext createRootApplicationContext() {
return super.createRootApplicationContext();
}
}
My two with #Configuration annotated files look like this:
My PersistenceConfig should be used for the ContextLoaderListener:
#Configuration
#EnableTransactionManagement(proxyTargetClass=true, mode=AdviceMode.PROXY)
public class PersistenceConfig{
Logger logger = LoggerFactory.getLogger(PersistenceConfig.class);
/** The Application Context. */
#Autowired
ApplicationContext context;
/**
* Gets the database url.
*
* #return the database url
*/
#Bean(name="databaseUrl")
public String getDatabaseUrl(){
return "jdbc:mysql://localhost/lifepulse";
}
/**
* Gets the session factory properties.
*
* #return the session factory properties
*/
#Bean(name="sessionFactoryProperties")
public Properties getSessionFactoryProperties(){
Properties props = new Properties();
props.put("hibernate.dialect", MySQL5InnoDBDialect.class.getName());
props.put("hibernate.hbm2ddl.auto", "create-drop");
props.put("hibernate.show_sql", "true");
props.put("hibernate.format_sql", "true");
return props;
}
/** The Constant ANNOTATED_CLASSES. */
#SuppressWarnings("unchecked")
private static final Class<? extends Serializable>[] ANNOTATED_CLASSES=new Class[]{
Melder.class,
LogEntry.class
};
/**
* Gets the annotated classes.
*
* #return the annotated classes
*/
private static Class<? extends Serializable>[] getAnnotatedClasses(){
return ANNOTATED_CLASSES;
}
/**
* Gets the data source.
* This bean represents the application's MYSQL datasource, without using xml.
*
* #return the data source
*/
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl(getDatabaseUrl());
dataSource.setUsername("lifepulse");
dataSource.setPassword("lifepulse");
return dataSource;
}
/**
* Session factory.
* This bean represents the Hibernate Session Factory. By declaring this bean
* it can easily be injected into Spring DAOs later on.
*
* #return the local session factory bean
*/
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setAnnotatedClasses(getAnnotatedClasses());
factory.setHibernateProperties(getSessionFactoryProperties());
factory.setDataSource(dataSource());
return factory;
}
#Bean()
public GenericDao genericDao() {
return new HibernateDaoImpl();
}
#Bean
PlatformTransactionManager txManager(){
HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory().getObject());
return htm;
}
}
My LifepulseWebConfig should be used for the DispatcherSerlvet:
#Configuration
#EnableWebMvc
#ComponentScan(value= {"com.ansiworks.lifepulse.controllers"})
#EnableTransactionManagement(proxyTargetClass=true, mode=AdviceMode.PROXY)
public class LifepulseWebConfig extends WebMvcConfigurerAdapter{
#Autowired
ApplicationContext context;
#Bean
ViewResolver viewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
//resolver.setPrefix("");
resolver.setSuffix(".jsp");
return resolver;
}
#Bean
MainService mainService(){
return new MainServiceImpl();
}
}
This configuration doesn't work. The dispathcer servlet doesn't load my PersistenceConfig, and so the beans are not herited to the LifepulseWebConfig.
But: When I add the PersistenceConfig to the class-array of the Method LifepulseServletInitializer#getServletConfigClasses() everything works fine.
However... I can't use sprin-security in this way....
What am I doing wrong!? Why are the config-classes returned by LifepulseServletInitializer#getRootConfigClasses() not read by the ContextLoaderListener?
I also tried it this way (without the LifepulseServletInitializer ) with the same effect. The beans of my PersistenceConfig are simply not loaded...
public class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(PersistenceConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(LifepulseWebConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Related
I want to configure Spring application with Hibernate. I tried this:
Main start method:
#Configuration
#EnableWebMvc
public class WebConfig implements WebApplicationInitializer, WebMvcConfigurer {
private BasicAuthenticationInterceptor basicAuthenticationInterceptor;
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(converter -> converter instanceof MappingJackson2XmlHttpMessageConverter);
converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
converters.add(new MappingJackson2XmlHttpMessageConverter(
((XmlMapper) createObjectMapper(Jackson2ObjectMapperBuilder.xml()))
.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)));
converters.add(new MappingJackson2HttpMessageConverter(
createObjectMapper(Jackson2ObjectMapperBuilder.json())));
}
private ObjectMapper createObjectMapper(Jackson2ObjectMapperBuilder builder) {
builder.indentOutput(true);
builder.modules(new JaxbAnnotationModule());
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.defaultUseWrapper(false);
return builder.build();
}
#Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(ContextDatasource.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
#Autowired
public void setBasicAuthenticationInterceptor(BasicAuthenticationInterceptor basicAuthenticationInterceptor) {
this.basicAuthenticationInterceptor = basicAuthenticationInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(basicAuthenticationInterceptor);
}
}
Hibernate configuration called from rootContext.register(ContextDatasource.class);:
#SpringBootApplication
#Configuration
#EnableTransactionManagement
public class ContextDatasource {
#Bean
public FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
return new FastJsonHttpMessageConverter();
}
#Bean
#Autowired
public HttpMessageConverters convertersToBeUsed(FastJsonHttpMessageConverter converter) {
return new HttpMessageConverters(converter);
}
#Bean
public LocalSessionFactoryBean getSessionFactory() throws NamingException {
final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setPackagesToScan(new String[] { "org.datalis.plugin.database.models" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource getDataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup("java:/global/production_gateway");
}
#Bean
public PlatformTransactionManager getHibernateTransactionManager() throws NamingException {
final HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(getSessionFactory().getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor getExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private final Properties hibernateProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MariaDBDialect");
hibernateProperties.setProperty("hibernate.show_sql", "true");
hibernateProperties.setProperty("hibernate.format_sql", "true");
return hibernateProperties;
}
}
But when I deploy the WAR file I get error:
Caused by: java.lang.NoSuchMethodException: org.springframework.boot.autoconfigure.http.HttpMessageConverters$$EnhancerBySpringCGLIB$$1d90bff9.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3302)
Full error stack:
https://pastebin.com/x30W2aws
Can you give advice where I'm wrong and how to fix the problem?
Do I need to implement the module startup with another configuration?
EDIT:
With Java 8 the code is working without above issue. With latest Java 10 I get the above exception. Do you know what configuration I need to do?
According to the Spring Boot release notes, Java 10 is supported by Spring Boot version 2.0.1 and up. Without a list of your dependencies it's impossible to know if this is the issue, but it does seem like a good place to start.
Are you running Boot v2.0.1 or higher?
I am following this link
https://github.com/kwon37xi/replication-datasource
I have implemented the code But STILL Both my service functions are using the same Database(one which is marked primary)
Service Class
public class TableService{
#Autowired
private Table1Repo t1Repo;
#Transactional(readOnly = false)
public void saveTable1(Table1 t,int a, Table1 t2){
try{
t1Repo.save(t2);
}
catch(Exception e){
System.out.println("Inside");
}
}
#Transactional(readOnly = true)
public Table1 getTable(int id){
return t1Repo.findOne(id);
}
}
Then Added two Class(from the link)
ReplicationRoutingDataSource
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
String dataSourceType = TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "read" : "write";
return dataSourceType;
}
}
WithRoutingDataSourceConfig
#Configuration
public class WithRoutingDataSourceConfig {
/*#Bean(destroyMethod = "shutdown")*/
#Bean
#Primary
#ConfigurationProperties(prefix="datasource.primary")
public DataSource writeDataSource() {
/* EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
.setName("routingWriteDb")
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("classpath:/writedb.sql");
return builder.build();*/
return DataSourceBuilder.create().build();
}
/* #Bean(destroyMethod = "shutdown")*/
#Bean
#ConfigurationProperties(prefix="datasource.secondary")
public DataSource readDataSource() {
/*EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
.setName("routingReadDb")
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("classpath:/readdb.sql");
return builder.build();*/
return DataSourceBuilder.create().build();
}
/**
* {#link org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource}는
* {#link org.springframework.beans.factory.InitializingBean}을 구현하므로,
* 명시적으로 afterPropertiesSet()메소드를 호출하거나
* 별도 #Bean으로 만들어 Spring Life Cycle을 타도록 해야 한다.
*/
#Bean
public DataSource routingDataSource(#Qualifier("writeDataSource") DataSource writeDataSource, #Qualifier("readDataSource") DataSource readDataSource) {
ReplicationRoutingDataSource routingDataSource = new ReplicationRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
dataSourceMap.put("write", writeDataSource);
dataSourceMap.put("read", readDataSource);
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(writeDataSource);
return routingDataSource;
}
/**
* {#link org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy}로 감싸서
* 트랜잭션 동기화가 이루어진 뒤에 실제 커넥션을 확보하도록 해준다.
*
* #param routingDataSource
* #return
*/
#Bean
public DataSource dataSource(#Qualifier("routingDataSource") DataSource routingDataSource) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
}
application.prop file
server.port=8089
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.show_sql=true
# Primary DataSource configuration
datasource.primary.url=jdbc:mysql://127.0.0.1:3306/jpa
datasource.primary.username=root
datasource.primary.password=root
# Any of the other Spring supported properties below...
# Secondary DataSource configuration
datasource.secondary.url=jdbc:mysql://127.0.0.1:3306/jpa2
datasource.secondary.username=root
datasource.secondary.password=root
Repository
public interface Table1Repo extends JpaRepository<Table1, Integer>{}
Issue is my both service functions are using the primary Database. What am I missing. I only have these class. Rest I have one Controller
Edited
I have made by code work by adding this class
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="com.example")
public class ReplicationDataSourceApplicationConfig {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(#Qualifier("dataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setDataSource(dataSource);
emfb.setPackagesToScan("com.example");
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
emfb.setJpaVendorAdapter(jpaVendorAdapter);
return emfb;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
I'm the writer of the link you refered.
Which data source do you use with Table1Repo?
You have to inject the specific bean "dataSource" to you JDBC call.
I guess #Primary "writeDataSource" is injected to your Repository.
Try to change #Primary to "dataSource" or find a way to inject "dataSource" to your repository.
you can also try in this way:
Spring Boot 2 with Multiple DataSource for Postgres Data Replication
and here is source code in github:
spring-boot-multi-data-source
The following link explans, how you can have multiple datasource
DB1 (write):
#Configuration
#ConfigurationProperties("spring.datasource-write")
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryWrite",
transactionManagerRef = "transactionManagerWrite",
basePackages = {"com.ehsaniara.multidatasource.repository.writeRepository"}
)
public class DataSourceConfigWrite extends HikariConfig {
public final static String PERSISTENCE_UNIT_NAME = "write";
#Bean
public HikariDataSource dataSourceWrite() {
return new HikariDataSource(this);
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryWrite(
final HikariDataSource dataSourceWrite) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(dataSourceWrite);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(MODEL_PACKAGE);
setJpaProperties(JPA_PROPERTIES);
}};
}
#Bean
public PlatformTransactionManager transactionManagerWrite(EntityManagerFactory entityManagerFactoryWrite) {
return new JpaTransactionManager(entityManagerFactoryWrite);
}
}
DB2 (read):
#Configuration
#ConfigurationProperties("spring.datasource-read")
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryRead",
transactionManagerRef = "transactionManagerRead",
basePackages = {"com.ehsaniara.multidatasource.repository.readRepository"}
)
public class DataSourceConfigRead extends HikariConfig {
public final static String PERSISTENCE_UNIT_NAME = "read";
#Bean
public HikariDataSource dataSourceRead() {
return new HikariDataSource(this);
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryRead(
final HikariDataSource dataSourceRead) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(dataSourceRead);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(MODEL_PACKAGE);
setJpaProperties(JPA_PROPERTIES);
}};
}
#Bean
public PlatformTransactionManager transactionManagerRead(EntityManagerFactory entityManagerFactoryRead) {
return new JpaTransactionManager(entityManagerFactoryRead);
}
}
the given entitymanager has no managedtype for my class, it is the wrong entitymanager.
i thought that by simply defining the model and repository scan to the specific namespace does handle this automatically, but as it seems i am wrong (?!). Does anybody know how to handle this problem?
He has the class something.application.model.User but uses the entitymanager with the managedtypes of the namespace something.manager.model.
public class JpaConfiguration {
#Configuration
#EntityScan(basePackages = { "something.application.model" })
#EnableJpaAuditing
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = { "something.application.model" }, repositoryFactoryBeanClass = RepositoryFactoryBean.class, entityManagerFactoryRef = "applicationEntityManagerFactory", transactionManagerRef = "applicationTransactionManager")
public static class ApplicationJpaConfiguration {
#Autowired
private ApplicationContext applicationContext;
/**
* The datasource used by the system. Configuration see
* /src/main/resources/application.properties.
*/
#Primary
#Bean(name="applicationDataSource")
#ConfigurationProperties(prefix = "spring.application.datasource")
public DataSource applicationDataSource() {
return new TenantAwareDataSource();
}
/**
* The transaction manager used by the application system.
*
* #param javax.sql.DataSource applicationDataSource
* #param javax.sql.DataSource loadTimeWeaver
* #return org.springframework.orm.jpa.JpaTransactionManager
*/
#Primary
#Bean(name="applicationTransactionManager")
public JpaTransactionManager applicationTransactionManager(LoadTimeWeaver loadTimeWeaver) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(applicationEntityManagerFactory(loadTimeWeaver).getObject());
return transactionManager;
}
/**
* The entity manager factory used by application system. Specific properties of the
* persistence provider eclipselink are configured here.
*/
#Primary
#Bean(name="applicationEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean applicationEntityManagerFactory(LoadTimeWeaver loadTimeWeaver) {
Map<String, String> jpaProperties = new HashMap<String, String>();
jpaProperties.put("eclipselink.ddl-generation", "create-or-extend-tables");
jpaProperties.put("eclipselink.session.customizer", HistoryBuildingSessionCustomizer.class.getName());
jpaProperties.put("eclipselink.logging.level.sql", "fine");
jpaProperties.put("eclipselink.logging.parameters", "true");
jpaProperties.put("eclipselink.temporal.mutable", "true");
jpaProperties.put("eclipselink.persistence-context.flush-mode", "commit");
jpaProperties.put("eclipselink.cache.shared.default", "false");
jpaProperties.put("eclipselink.query-results-cache", "false");
jpaProperties.put("eclipselink.target-database", "MySQL");
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(applicationDataSource());
entityManagerFactoryBean.setJpaVendorAdapter(new EclipseLinkJpaVendorAdapter());
entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
entityManagerFactoryBean.setLoadTimeWeaver(loadTimeWeaver);
entityManagerFactoryBean.setPersistenceUnitName("applicationPersistenceUnit");
return entityManagerFactoryBean;
}
/**
* A support object that wraps all available repositories and has some
* convenience functions.
*/
#Bean
public Repositories repositories() {
return new Repositories(applicationContext);
}
}
#Configuration
#EntityScan(basePackages = { "something.manager.model" })
#EnableJpaAuditing
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = { "something.manager.model" }, repositoryFactoryBeanClass = RepositoryFactoryBean.class, entityManagerFactoryRef = "managerEntityManagerFactory", transactionManagerRef = "managerTransactionManager")
public static class ManagerJpaConfiguration {
#Autowired
private ApplicationContext applicationContext;
/**
* The datasource used by the system. Configuration see
* /src/main/resources/application.properties.
*/
#Bean(name="managerDataSource")
#ConfigurationProperties(prefix = "spring.manager.datasource")
public DataSource managerDataSource() {
return new BasicDataSource();
}
/**
* The transaction manager used by the manager system.
*
* #param javax.sql.DataSource managerDataSource
* #param javax.sql.DataSource loadTimeWeaver
* #return org.springframework.orm.jpa.JpaTransactionManager
*/
#Bean(name="managerTransactionManager")
public JpaTransactionManager managerTransactionManager(DataSource managerDataSource, LoadTimeWeaver loadTimeWeaver) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(managerEntityManagerFactory(managerDataSource, loadTimeWeaver).getObject());
return transactionManager;
}
/**
* The entity manager factory used by manager system. Specific properties of the
* persistence provider eclipselink are configured here.
*/
#Bean(name="managerEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean managerEntityManagerFactory(DataSource managerDataSource, LoadTimeWeaver loadTimeWeaver) {
Map<String, String> jpaProperties = new HashMap<String, String>();
jpaProperties.put("eclipselink.ddl-generation", "create-or-extend-tables");
jpaProperties.put("eclipselink.session.customizer", HistoryBuildingSessionCustomizer.class.getName());
jpaProperties.put("eclipselink.logging.level.sql", "fine");
jpaProperties.put("eclipselink.logging.parameters", "true");
jpaProperties.put("eclipselink.temporal.mutable", "true");
jpaProperties.put("eclipselink.persistence-context.flush-mode", "commit");
jpaProperties.put("eclipselink.cache.shared.default", "false");
jpaProperties.put("eclipselink.query-results-cache", "false");
jpaProperties.put("eclipselink.target-database", "MySQL");
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(managerDataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new EclipseLinkJpaVendorAdapter());
entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
entityManagerFactoryBean.setLoadTimeWeaver(loadTimeWeaver);
return entityManagerFactoryBean;
}
/**
* A support object that wraps all available repositories and has some
* convenience functions.
*/
#Bean
public Repositories repositories() {
return new Repositories(applicationContext);
}
}
}
Currently im working on a new Spring 4 + Thymeleaf + Security project without XML bean files. Somehow the localization property files are not loading and i cant find anything about them in the log files.
/**
* Generates the i18n language loader.
*
* #return
*/
#Bean(name="messageSource")
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
resource.setBasename("classpath:messages");
resource.setDefaultEncoding("UTF-8");
resource.setUseCodeAsDefaultMessage(true);
resource.setFallbackToSystemLocale(false);
return resource;
}
/**
* Generates the i18n language changer parameter.
*
* #return
*/
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
/**
* Genereates the default i18n language.
*
* #return
*/
#Bean(name = "localeResolver")
public SessionLocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(new Locale("en"));
return localeResolver;
}
/**
* Generates the template resolver for thymeleaf.
*
* #return
*/
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setCacheable(false);
return resolver;
}
/**
* Generates the template engine for thymeleaf.
*
* #return
*/
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.addDialect(new SpringSecurityDialect());
return engine;
}
/**
* Generates the view resolver from Spring MVC with thymeleaf intergrated.
*
* #return
*/
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
viewResolver.setViewNames(new String[]{"*"});
viewResolver.setCache(false);
return viewResolver;
}
/**
* Add resources.
*
* #param registry
*/
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**").addResourceLocations("/WEB-INF/assets/");
}
/**
* Add interceptors.
*
* #param registry
*/
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
File names in the src/main/resources/ folder:
messages.properties
messages_en.properties
messages_nl.properties
Is there anyone who can help me out?
Slash character "/" denoting the classpath root is missing in the basename pattern of your ReloadableResourceBundleMessageSource.
resource.setBasename("classpath:/messages");
Javabase Config:
#Configuration
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new
ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setCacheSeconds(10);
return messageSource;
}
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setValidationMessageSource(messageSource());
return factoryBean;
}
#Override
public Validator getValidator() {
return localValidatorFactoryBean();
}
}
XmLbase Config:
Note: You should add mvc namespaces already.
<mvc:annotation-driven validator="validatorFactoryBean"/>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages"/>
</bean>
<bean id="validatorFactoryBean"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource"/>
</bean>
Note: If you're trying to load from "webapp" folder so there is no need to use "classpath:" prefix.
I neded up with the following configuration for my dev env:
application.properties:
spring.messages.basename=file:src/main/resources/messages
spring.messages.cache-duration=0
Java configuration:
#Bean
#ConfigurationProperties(prefix = "spring.messages")
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
#Bean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME)
public ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource( MessageSourceProperties properties ) {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
if (StringUtils.hasText( properties.getBasename() )) {
messageSource.setBasenames( StringUtils.commaDelimitedListToStringArray( StringUtils.trimAllWhitespace( properties.getBasename() ) ) );
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding( properties.getEncoding().name() );
}
messageSource.setFallbackToSystemLocale( properties.isFallbackToSystemLocale() );
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis( cacheDuration.toMillis() );
}
messageSource.setAlwaysUseMessageFormat( properties.isAlwaysUseMessageFormat() );
messageSource.setUseCodeAsDefaultMessage( properties.isUseCodeAsDefaultMessage() );
return messageSource;
}
Exaplanation:
spring.messages.basename=file:src/main/resources/messages => read files directly from source
spring.messages.cache-duration=0 => disable caching for files to be reloaded. Default is -1 which means cache forever.
The ReloadableResourceBundleMessageSource bean is a direct copy from spring's MessageSourceAutoConfiguration.java impl, but using ReloadableResourceBundleMessageSource instead as message source.
Bean needs to have name AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME to disable default auto-configuration.
I have a Spring MVC application with Thymeleaf configured to use fragments (NO tiles!) and all files have .html extension. Everything is working fine.
Now i'm trying to setup Webflow but, when i call my webflow url, i get 404 error as he tries to load a JSP view instead of html (outside flow, everything is fine):
HTTP Status 404 - /app/WEB-INF/views/contest/contest-step1.jsp
I know that put kilometers of line of code isn't good, but honestly i don't know which pieces are interesting and which no.
ThymeleafConfig:
#Configuration
public class ThymeleafConfig {
#Bean
public TemplateResolver templateResolver() {
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
/**
* only on development machine
*/
templateResolver.setCacheable(false);
return templateResolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
Set<IDialect> dialects = new HashSet<IDialect>();
dialects.add(springSecurityDialect());
templateEngine.setAdditionalDialects(dialects);
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
return resolver;
}
#Bean
public SpringSecurityDialect springSecurityDialect(){
SpringSecurityDialect dialect = new SpringSecurityDialect();
return dialect;
}
}
WebAppConfig:
#Configuration
#EnableWebMvc
#ComponentScan("com.myapp")
public class WebAppConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
// Maps resources path to webapp/resources
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
/**************************************************
*
* Web Flow: wizard contest
*
*/
#Autowired
private WebFlowConfig webFlowConfig;
/**
* Maps request paths to flows in the flowRegistry;
* e.g. a path of /hotels/booking looks for a flow with id "hotels/booking"
*
* Configuring this mapping allows the Dispatcher to map application resource paths
* to flows in a flow registry.
* For example, accessing the resource path /hotels/booking would result in a
* registry query for the flow with id hotels/booking.
* If a flow is found with that id,
* that flow will handle the request. If no flow is found, the next handler mapping in the
* Dispatcher's ordered chain will be queried or a "noHandlerFound" response will be returned.
*/
#Bean
public FlowHandlerMapping flowHandlerMapping() {
FlowHandlerMapping handlerMapping = new FlowHandlerMapping();
handlerMapping.setOrder(-1); //0 ?
handlerMapping.setFlowRegistry(this.webFlowConfig.flowRegistry());
return handlerMapping;
}
#Bean
public FlowHandlerAdapter flowHandlerAdapter() {
FlowHandlerAdapter handlerAdapter = new FlowHandlerAdapter();
handlerAdapter.setFlowExecutor(this.webFlowConfig.flowExecutor());
handlerAdapter.setSaveOutputToFlashScopeOnRedirect(true);
return handlerAdapter;
}
#Bean(name="contest/add") //defined in in WebFlowConfig
public ContestFlowHandler ContestFlowHandler() {
return new ContestFlowHandler();
}
/* TODO
#Bean
public AjaxThymeleafViewResolver tilesViewResolver() {
AjaxThymeleafViewResolver viewResolver = new AjaxThymeleafViewResolver();
viewResolver.setViewClass(FlowAjaxThymeleafTilesView.class);
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
} */
}
WebFlowConfig:
#Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
#Autowired
private ThymeleafConfig thymeleafConfig;
//private WebFlowConfig WebAppConfig;
#Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder()
.addFlowLocation("/WEB-INF/views/gare/gare-add-flow.xml", "gare/add")
.build();
}
#Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry())
.setMaxFlowExecutions(5)
.setMaxFlowExecutionSnapshots(30)
.build();
}
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setViewFactoryCreator(mvcViewFactoryCreator())
.build();
}
#Bean
public MvcViewFactoryCreator mvcViewFactoryCreator() {
MvcViewFactoryCreator factoryCreator = new MvcViewFactoryCreator();
factoryCreator.setViewResolvers(Arrays.<ViewResolver>asList(this.thymeleafConfig.thymeleafViewResolver()));
factoryCreator.setUseSpringBeanBinding(true);
return factoryCreator;
}
}
I found a trick that gives me a quick solution, but honestly i would like don't do this, but correctly configure the resolver. The trick is to set the view file in flow xml file:
<view-state id="contest-step1" model="contest" view="/WEB-INF/views/contest/contest-step1.html"></view-state>
thanks
Try the following in flowRegistry method.
return getFlowDefinitionRegistryBuilder(flowBuilderServices())
instead of
return getFlowDefinitionRegistryBuilder()
Hope this helps.
I am not sure but kindly look into the following part of your code
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
it is pointing to .jsp whatever you call in controller it will append .jsp
Also your controller code is not here url mapping should be like /login.html
I am not much familier with thymeleaf and spring-webflow-2