Spring #Cacheable doesn't work - spring

I have a service with next methods:
public Optional<Test> getTestWithId100() {
return get(100);
}
#Cacheable(value = "test", key = "'1'")
public Optional<Test> get(long id) {
log.error("not from cache");
return testRepository.findOneById(id);
}
I call method getTestWithId100 from controller, but it only get fresh value.
#Slf4j
#Configuration
#EnableCaching
#AutoConfigureAfter(value = { MetricsConfiguration.class, DatabaseConfiguration.class })
public class CacheConfiguration {
#PersistenceContext
private EntityManager entityManager;
private final MetricRegistry metricRegistry;
private net.sf.ehcache.CacheManager cacheManager;
#Inject
public CacheConfiguration(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
#PreDestroy
public void destroy() {
log.info("Remove Cache Manager metrics");
SortedSet<String> names = metricRegistry.getNames();
names.forEach(metricRegistry::remove);
log.info("Closing Cache Manager");
cacheManager.shutdown();
}
#Bean
public CacheManager cacheManager(Properties properties) {
log.debug("Starting Ehcache");
cacheManager = net.sf.ehcache.CacheManager.create();
cacheManager.getConfiguration().setMaxBytesLocalHeap(properties.getCache().getEhcache().getMaxBytesLocalHeap());
log.debug("Registering Ehcache Metrics gauges");
entityManager.getMetamodel().getEntities().forEach(entity -> {
String name = entity.getName();
if (name == null || entity.getJavaType() != null)
name = entity.getJavaType().getName();
Assert.notNull(name, "entity cannot exist without a identifier");
net.sf.ehcache.Cache cache = cacheManager.getCache(name);
if (cache != null)
cacheManager.replaceCacheWithDecoratedCache(cache, InstrumentedEhcache.instrument(metricRegistry, cache));
});
EhCacheCacheManager ehCacheManager = new EhCacheCacheManager();
ehCacheManager.setCacheManager(cacheManager);
return ehCacheManager;
}
}
part of ehcache.xml:
<cache name="test" eternal="true"/>
Why it doesn't work? I tried with different keys but without success.

Spring annotations work by proxying / enhancing your classes. The one limitation in this system is when you call a method on the same bean, then that call is not intercepted by the system and thus no annotation based modifications will be applied.

I think that you are wrong in cache config, let's consider code below (it works fine for me):
#Bean
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheCacheManager().getObject());
}
#Bean
public EhCacheManagerFactoryBean ehCacheCacheManager() {
EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
cmfb.setShared(true);
return cmfb;
}
And of course, ehcache.xml:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true"
monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir" />
<cache name="movieFindCache"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
This config must help you, if it dosn't solve the problem, notify me. HTH

Related

Spring boot jpa second level cache not being use even after configuring

We have set up JPA second-level cache but not working with the following configuration cache is configured properly as per logging but it not cache the database result and always fetching data from the database.
11:44:07.454 [main] [UserId: ] WARN net.sf.ehcache.Cache - Cache: com.Record has a maxElementsInMemory of 0. In Ehcache 2.0 this has been changed to mean a store with no capacity limit. Set it to 1 if you want no elements cached in memory.
ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true">
<defaultCache eternal="false" timeToLiveSeconds="10000" memoryStoreEvictionPolicy="LFU" statistics="true" maxElementsInMemory="10000" overflowToDisk="false" />
<cache maxElementsInMemory="10000" name="Record" eternal="false" timeToIdleSeconds="10000" timeToLiveSeconds="10000" memoryStoreEvictionPolicy="LFU" statistics="true" />
</ehcache>
Enabled spring boot Caching in Application.java
#SpringBootApplication
#EnableScheduling
#EnableCaching
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#PostConstruct
public void init(){
// Setting Spring Boot SetTimeZone
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Entity Record.java
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "t_record")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "c_discriminator", discriminatorType = DiscriminatorType.STRING)
#Cacheable
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE,region = "Record")
#NamedQueries({#NamedQuery(name = Record.QUERY_findAllByType, query = "SELECT r FROM Record r WHERE TYPE(r) = ?1 ORDER BY r.date DESC, r.id DESC")})
public abstract class Record implements Cloneable, XmlOutputEntity {
/**
* Class UID
*/
private static final long serialVersionUID = 7146308281386143748L;
public static final String QUERY_findAllByType = "findAllRecordByType";
}
Enabled second-level cache and query cache in the database configuration.
#Configuration
public class DatabaseConfig {
protected PropConfig propConfig;
#Autowired
public DatabaseConfig(PropConfig propConfig) {
this.propConfig = propConfig;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(propConfig.getDatabaseDriver());
dataSource.setUrl(propConfig.getDatabaseUrl());
dataSource.setUsername(propConfig.getDatabaseUsername());
dataSource.setPassword(propConfig.getDatabasePassword());
return dataSource;
}
#Bean
public LocalValidatorFactoryBean validator() {
final LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
return validator;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource);
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.test");
Properties props = new Properties();
props.put("logging.level.org.hibernate.SQL", "true");
props.put("logging.level.org.hibernate.type.descriptor.sql.BasicBinder", "TRACE");
props.put("show-sql", "true");
lef.setJpaProperties(props);
lef.getJpaPropertyMap().put("jadira.usertype.autoRegisterUserTypes", "true");
lef.getJpaPropertyMap().put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
lef.getJpaPropertyMap().put("hibernate.temp.use_jdbc_metadata_defaults", "false");
lef.getJpaPropertyMap().put("hibernate.cache.use_second_level_cache", "true");
lef.getJpaPropertyMap().put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");
lef.getJpaPropertyMap().put("hibernate.cache.use_query_cache", "true");
lef.setSharedCacheMode(SharedCacheMode.ALL);
return lef;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(propConfig.isShowSql());
hibernateJpaVendorAdapter.setGenerateDdl(propConfig.isGenerateDdl());
hibernateJpaVendorAdapter.setDatabase(propConfig.getVendor());
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}
Query caching has to be enabled on a case by case basis by setting the org.hibernate.cacheable hint to true. I think with Spring Data JPA you have to annotate the repository method with #QueryHint(name = "org.hibernate.cacheable", value = "true")

EHCACHE 3.x spring boot no of value in cache

I am trying to use ehcache.
#Configuration
#EnableCaching
public class CacheConfig {
#Bean
public JCacheCacheManager jCacheCacheManager() throws IOException {
return new JCacheCacheManager(cacheManager());
}
#Bean(destroyMethod = "close")
public javax.cache.CacheManager cacheManager() throws IOException {
XmlConfiguration xmlConfig = new XmlConfiguration(new ClassPathResource("ehcache.xml").getURL());
EhcacheCachingProvider provider = (EhcacheCachingProvider) Caching.getCachingProvider();
return provider.getCacheManager(provider.getDefaultURI(), xmlConfig);
}
}
#Service
public class AvengersService {
#Cacheable(cacheNames="avengers" , key="#id")
public List<String> getAvengers(String id) {
System.out.println("Loading avengers");
return Arrays.asList(id.toString(),"Captain America", "Hulk", "Iron Man", "Thor");
}
}
#SpringBootApplication
#ComponentScan
public class DemoApplication {
#Autowired
private CacheManager cacheManager;
#Autowired
private AvengersService avengersService;
public static void main(String[] args) {
//list all the caching provider
Iterator<CachingProvider> iterator = Caching.getCachingProviders(Caching.getDefaultClassLoader()).iterator();
while(iterator.hasNext()) {
CachingProvider provider = iterator.next();
System.out.println(provider);
if ((provider instanceof HazelcastCachingProvider)) {
iterator.remove();
}
}
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
JCacheCache cache = (JCacheCache) cacheManager
.getCache("avengers");
for(Long i=1L;i<50001;i++) {
List<String> t=avengersService.getAvengers(String.valueOf(i)+"a");
cache.put(String.valueOf(i)+"a", avengersService.getAvengers(String.valueOf(i)+"a"));
}
System.out.println();
Cache<Object, Object> e= ( cache.getNativeCache());
Iterator<Cache.Entry<Object, Object>> iterator = ( e).iterator();
int count=1;
while (iterator.hasNext()) {
Cache.Entry<Object, Object> t = iterator.next();
count++;
System.out.println("key :" + t.getKey() + " value " + t.getValue());
}
System.out.println(count);
final MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
final Set<ObjectInstance> cacheBeans = beanServer.queryMBeans(ObjectName.getInstance("javax.cache:type=CacheStatistics,CacheManager=*,Cache=*"), null);
for ( ObjectInstance cacheBean : cacheBeans ) {
final CacheStatisticsMXBean cacheStatisticsMXBean = MBeanServerInvocationHandler.newProxyInstance(beanServer, cacheBean.getObjectName(), CacheStatisticsMXBean.class, false);
System.out.println("Gets: " + cacheStatisticsMXBean.getCacheGets());
System.out.println("Hits: " + cacheStatisticsMXBean.getCacheHits());
System.out.println("Misses: " + cacheStatisticsMXBean.getCacheMisses());
System.out.println("Removals: " + cacheStatisticsMXBean.getCacheRemovals());
System.out.println("Evictions: " + cacheStatisticsMXBean.getCacheEvictions());
System.out.println("Avg Get Time: " + cacheStatisticsMXBean.getAverageGetTime());
System.out.println("Avg Put Time: " + cacheStatisticsMXBean.getAveragePutTime());
System.out.println("Avg Remove Time: " + cacheStatisticsMXBean.getAverageRemoveTime());
}
};
}
}
public class MyCacheEntryListener<K, V> implements CacheEventListener<K, V>, Serializable {
#Override
public void onEvent(CacheEvent<? extends K, ? extends V> event) {
// TODO Auto-generated method stub
if (EventType.UPDATED == event.getType()) {
System.out.println("Received a " + event);
}
}
}
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<service>
<jsr107:defaults enable-management="true" enable-statistics="true"/>
</service>
<cache alias="avengers">
<key-type>java.lang.String</key-type>
<value-type>java.util.Arrays$ArrayList</value-type>
<expiry>
<none/>
</expiry>
<listeners>
<listener>
<class>com.example.demo.MyCacheEntryListener</class>
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
<event-ordering-mode>UNORDERED</event-ordering-mode>
<events-to-fire-on>UPDATED</events-to-fire-on>
</listener>
</listeners>
<resources>
<heap unit="entries">2</heap>
<offheap unit="MB">10</offheap>
</resources>
</cache>
</config>
spring.cache.jcache.config=classpath:ehcache.xml
Blockquote
While starting my appilication its prints 2 cache provider.
HazelcastCachingProvider{delegate=HazelcastServerCachingProvider{hazelcastInstance=null}}
org.ehcache.jsr107.EhcacheCachingProvider#1f57539
Why its initialising the 2 cache provider?
if its default how can I disable these?
I have added 50000 object in the cache but while iterating it i am only getting "36406" object why?
There are a lot of moving parts here. You don't need to configure the CacheManager, it is done automatically with the spring.cache.jcache.config property. Remove the 2 #Bean methods.
If you need to access the javax.cache.CacheManager, you can inject JCacheCacheManager and it'll give you access to the native one it wraps.
There are several JSR107 implementations on the classpath so that API call gives you what's available, this is nothing Spring Boot has control over.

NullPointerException in Spring Autowiring Repository

I'm getting an NPE when I'm trying to auto wiring a Repository in my managed bean. This is my first project trying to use java configurations instead of xml configurations.
I'm not instantiating my self the repository and all the packages are in the spring context, even I can see in the log the instantiating of the repository.
Please, someone, give me a hand to figure out what's missing in my configuration
Using spring version 5.1.0.RELEASE and
<dependency>
<groupId>com.googlecode.genericdao</groupId>
<artifactId>dao-hibernate</artifactId>
<version>1.2.0</version>
</dependency>
1) WebMvcConfig
#Configuration
#EnableWebMvc
#ComponentScan({
"com.config","com.gedificios",
})
public class WebMvcConfig implements WebMvcConfigurer {
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver resolver = new
InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}
2) PersistenceJPAConfig
#Configuration
#EnableTransactionManagement
#PropertySource({"classpath:database.properties"})
#ComponentScan({"com.config","com.gedificios.core", "com.gedificios.persistence", "com.gedificios.view"})
#EntityScan("com.gedificios.persistence.entities")
#EnableJpaRepositories(basePackages = "com.gedificios.core.repository")
public class PersistenceJPAConfig {
#Autowired
private Environment env;
public PersistenceJPAConfig() {
super();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setPersistenceXmlLocation("classpath*:WEB-INF/persistence.xml");
entityManagerFactoryBean.setPersistenceUnitName("persistenceUnit");
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setJpaProperties(additionalProperties());
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabasePlatform(env.getProperty("hibernate.dialect"));
vendorAdapter.setShowSql(Boolean.getBoolean(env.getProperty("hibernate.show_sql")));
vendorAdapter.setGenerateDdl(Boolean.getBoolean(env.getProperty("hibernate.generateDdl")));
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
return entityManagerFactoryBean;
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
return hibernateProperties;
}
#Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("hibernate.connection.driver_class"));
dataSource.setUrl(env.getProperty("hibernate.connection.url"));
dataSource.setUsername(env.getProperty("hibernate.connection.username"));
dataSource.setPassword(env.getProperty("hibernate.connection.password"));
return dataSource;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
#Primary
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
#Bean
public JPAAnnotationMetadataUtil getMetadataUtil() {
return new JPAAnnotationMetadataUtil();
}
#Bean
public JPASearchProcessor getJPASearchProcessor(JPAAnnotationMetadataUtil jpaAnnotationMetadataUtil) {
return new JPASearchProcessor(jpaAnnotationMetadataUtil);
}
}
3) Persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source/>
<class>com.gedificios.persistence.entities.Contrato</class>
<class>com.gedificios.persistence.entities.Edificio</class>
<class>com.gedificios.persistence.entities.ubigeo.Pais</class>
<class>com.gedificios.persistence.entities.ubigeo.Departamento</class>
<class>com.gedificios.persistence.entities.ubigeo.Provincia</class>
<class>com.gedificios.persistence.entities.ubigeo.Distrito</class>
<properties>
<property name="hibernate.dialect" value="${hibernate.dialect}"/>
<property name="hibernate.connection.charSet" value="UTF-8"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.use_jdbc_metadata_defaults" value="false"/>
</properties>
</persistence-unit>
</persistence>
4) PaisRepository
#Repository
public class PaisRepository extends BaseDAO<PaisDTO, String> {
#Autowired
DataSource dataSource;
private static final Logger LOG = LoggerFactory.getLogger(PaisRepository.class);
#PostConstruct
public void init() {
LOG.info("\n/***************** INICIALIZANDO REPOSITORY DE PAÍS /*****************\n");
}
public List<PaisDTO> getAllPaises() {
List< PaisDTO> items = null;
StringBuffer sql = new StringBuffer();
sql.append(" SELECT ");
sql.append(" P.id_pais \"idPais\" , ");
sql.append(" P.nombre_pais \"nombrePais\" ");
sql.append(" FROM ubigeo_pais as P order by p.nombre_pais");
System.out.println(sql);
org.hibernate.Query<PaisDTO> query = em().unwrap(Session.class).createSQLQuery(sql.toString()).setResultTransformer(new JpaHbmBeanResultTransformer(PaisDTO.class));
items = query.list();
return items;
}
}
4.1) Log evidence
06:24:50.197 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getJPASearchProcessor'
06:24:50.198 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'getMetadataUtil'
06:24:50.198 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'getJPASearchProcessor' via factory method to bean named 'getMetadataUtil'
06:24:50.199 [localhost-startStop-1] INFO com.gedificios.core.repository.PaisRepository -
/***************** INICIALIZANDO REPOSITORY DE PAÍS /*****************
06:24:50.201 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'DTOMapperServiceDozerImpl'
06:24:50.201 [localhost-startStop-1] DEBUG org.dozer.DozerInitializer - Tried to perform initialization when Dozer was already started.
06:24:50.201 [localhost-startStop-1] INFO org.dozer.DozerBeanMapper - Initializing a new instance of dozer bean mapper.
5) ManagedBean
#ManagedBean
#Scope("view")
#Component
public class AdminEdificios extends FindCrudBeanBase {
#Autowired
ApplicationContext applicationContext;
#Autowired
PaisRepository paisRepository;
private static final Logger LOG = LoggerFactory.getLogger(AdminEdificios.class);
private List<PaisDTO> listaPaises;
private List<DepartamentoDTO> listaDepartamentos;
private List<ProvinciaDTO> listaDistritos;
private List<DistritoDTO> listaProvincias;
private String codigoPaisSeleccionado;
private String codigoDepartamentoSeleccionado;
private String codigoProvinciaSeleccionada;
private String codigoDistritoSeleccionado;
#PostConstruct
public void init() {
listaPaises = paisRepository.findAll();
listaDepartamentos = new ArrayList<DepartamentoDTO>();
listaDepartamentos.add(new DepartamentoDTO("001","001","Lima"));
}
...
getters & setters
}
The NPE is exactly here
listaPaises = paisRepository.findAll();

i can't reach my method with primefaces ajax

i have an issue with my project, i'm using spring , jsf and primefaces, the problem is when i wish to call a method with ajax request, i can't and i have no error message to help me found the problem this is my first configuration with spring and jsf with annotations.
help me please .
my configuration:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class AppConfig {
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306 /carouzetDb?useUnicode=true&useJDBCCompliantTimezoneShift=true& useLegacyDatetimeCode=false&serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
private Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(
"hibernate.hbm2ddl.auto", "create");
hibernateProperties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return hibernateProperties;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan("com.crouzet.carouzetroject.entities");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager
= new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
}
public class MvcContentInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.setServletContext(servletContext);
servletContext.addListener(new ContextLoaderListener(ctx));
}
}
jsf configuration :
faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<application>
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
</application>
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.FileUploadRenderer</renderer-type>
<renderer-class>com.crouzet.carouzetroject.FileUploadRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>
and here is the example where i call my method:
#ManagedBean(name = "helloBean")
#ViewScoped
#Component
public class HelloBean {
List<Salarie> salaries;
List<Categorie> categories;
List<Poste> postes;
Production production;
Categorie selectedCat;
#Autowired
private CategorieDAO categorieDAO;
#Autowired
private SalarieDAO salarieDAO;
#PostConstruct
public void init(){
categories=categorieDAO.allCategories();
System.out.println("==========>>>FROM INIT"+categories.size());
}
public void actualiser(){
categories=categorieDAO.allCategories();
}
public void listeDesSalaries(){
System.out.println("========AAAAAAAAAAAAAA========>>>>");
if(selectedCat!=null)
salaries=salarieDAO.salariesParCategorie(selectedCat);
}
public List<Salarie> getSalaries() {
return salaries;
}
public void setSalaries(List<Salarie> salaries) {
this.salaries = salaries;
}
public List<Categorie> getCategories() {
return categories;
}
public void setCategories(List<Categorie> categories) {
this.categories = categories;
}
public List<Poste> getPostes() {
return postes;
}
public void setPostes(List<Poste> postes) {
this.postes = postes;
}
public Production getProduction() {
return production;
}
public void setProduction(Production production) {
this.production = production;
}
public Categorie getSelectedCat() {
return selectedCat;
}
public void setSelectedCat(Categorie selectedCat) {
this.selectedCat = selectedCat;
}
}
and here is my page
<h:form>
<p:commandButton action="#{helloBean.actualiser()}" value="Actualiser" update="mesListes"/>
<p:panelGrid id="mesListes" columns="2">
<p:outputLabel value="Ligne de fabrication"/>
<p:selectOneMenu id="categori" value="#{helloBean.selectedCat}">
<f:selectItem itemLabel="Select One" itemValue="" />
<f:selectItems value="#{helloBean.categories}" var="cat" itemLabel="#{cat.nom}" itemValue="#{cat}" />
<p:ajax listener="#{helloBean.listeDesSalaries()}" />
</p:selectOneMenu>
</p:panelGrid>
</h:form>

Spring WebFlow (seemingly) randomly stops working in Spring Boot app

I have a flow which seemed to be working fine until yesterday, when suddenly I started getting the following exception in my HTML page that maps to the first state in my flow:
org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'flowScope' cannot be found on null
The offending line of code was:
<h3 th:text="${flowRequestContext.flowScope}"/>
Further investigation showed that none of the flow variables are available anymore. Furthermore if I put print statements into the Service which the flow makes various calls to, I can see that none of these methods are being called anymore - it's like the flow just isn't running at all.
This was working fine previously. I even reverted all of my local changes to a previously stable version of the code, and the same issue was happening there as well. The only thing that seemed to temporarily get around the problem was to restart my computer - the problem disappeared for a short while but then came back.
To be honest I'm completely out of ideas as to what could have started causing such an intermittent problem. I was thinking along the lines of a stale Java process running in the background interfering with future runs of the application, but have checked for and killed off any remaining process in between deploys to no avail.
I have included what I hope are the relevant file below. Any help resolving this issue would be very much appreciated.
checkout.xml
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<on-start>
<set name="flowScope.paymentMethods" value="checkoutWidgetService.getPaymentMethods()"/>
<set name="flowScope.deliveryAddress" value="checkoutWidgetService.getDeliveryAddress()"/>
<set name="flowScope.sessionId" value="externalContext.nativeRequest.session.id"/>
</on-start>
<view-state id="payment-methods" view="payment-methods">
<transition on="selectPaymentMethod" to="new-details">
<evaluate expression="checkoutWidgetService.getCardDetails(requestParameters.type)" result="flowScope.cardDetails"/>
</transition>
</view-state>
<view-state id="new-details" view="new-details">
<transition on="submitDetails" to="summary">
<evaluate expression="checkoutWidgetService.buildCardDetails(requestParameters)" result="flowScope.cardDetails"/>
</transition>
</view-state>
<view-state id="summary" view="summary">
<transition on="completeCheckout" to="redirect">
<evaluate expression="checkoutWidgetService.completeCheckout(externalContext.nativeRequest.session, flowRequestContext, flowScope.cardDetails)"/>
</transition>
<transition on="cancelCheckout" to="redirect">
<evaluate expression="checkoutWidgetService.cancelCheckout(externalContext.nativeRequest.session, flowRequestContext)"/>
</transition>
</view-state>
<end-state id="redirect" view="externalRedirect:contextRelative:/payments/checkout-widgets/end"/>
</flow>
WebflowConfig.java
#Configuration
#AutoConfigureAfter(MvcConfig.class)
public class WebflowConfig extends AbstractFlowConfiguration {
#Autowired
private SpringTemplateEngine templateEngine;
#Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry())
.addFlowExecutionListener(new SecurityFlowExecutionListener())
.build();
}
#Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder(flowBuilderServices())
.addFlowLocation("classpath:/templates/checkout.xml", "payments/checkout-widget/start")
.build();
}
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setViewFactoryCreator(mvcViewFactoryCreator())
.setDevelopmentMode(true)
.build();
}
#Bean
public FlowController flowController() {
FlowController flowController = new FlowController();
flowController.setFlowExecutor(flowExecutor());
return flowController;
}
#Bean
public FlowHandlerMapping flowHandlerMapping() {
FlowHandlerMapping flowHandlerMapping = new FlowHandlerMapping();
flowHandlerMapping.setFlowRegistry(flowRegistry());
flowHandlerMapping.setOrder(-1);
return flowHandlerMapping;
}
#Bean
public FlowHandlerAdapter flowHandlerAdapter() {
FlowHandlerAdapter flowHandlerAdapter = new FlowHandlerAdapter();
flowHandlerAdapter.setFlowExecutor(flowExecutor());
flowHandlerAdapter.setSaveOutputToFlashScopeOnRedirect(true);
return flowHandlerAdapter;
}
#Bean
public AjaxThymeleafViewResolver thymeleafViewResolver() {
AjaxThymeleafViewResolver viewResolver = new AjaxThymeleafViewResolver();
viewResolver.setViewClass(FlowAjaxThymeleafView.class);
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
#Bean
public MvcViewFactoryCreator mvcViewFactoryCreator() {
List<ViewResolver> viewResolvers = new ArrayList<>();
viewResolvers.add(thymeleafViewResolver());
MvcViewFactoryCreator mvcViewFactoryCreator = new MvcViewFactoryCreator();
mvcViewFactoryCreator.setViewResolvers(viewResolvers);
mvcViewFactoryCreator.setUseSpringBeanBinding(true);
return mvcViewFactoryCreator;
}
}
CheckoutWidgetSessionMvcController.java
#Controller
#RequestMapping("/payments/checkout-widgets")
public class CheckoutWidgetSessionMvcController {
#Inject
private CheckoutWidgetService service;
#RequestMapping(value = {"/start"}, method = RequestMethod.GET)
public ModelAndView paymentMethods() {
return new ModelAndView("payment-methods", null);
}
#RequestMapping(value = "/end", method = RequestMethod.GET)
public String invalidateSession(HttpSession session) {
service.invalidateSession(session);
return "dummy-redirect-post";
}
}
CheckoutWidgetService.java
public interface CheckoutWidgetService {
List<PaymentMethod> getPaymentMethods();
CardDetails getCardDetails(String name);
CardDetails buildCardDetails(LocalParameterMap params);
String getDeliveryAddress();
void completeCheckout(HttpSession session, RequestContext context, CardDetails cardDetails);
void cancelCheckout(HttpSession session, RequestContext context);
void invalidateSession(HttpSession session);
}
CheckoutWidgetServiceImpl.java
#Service("checkoutWidgetService")
public class CheckoutWidgetServiceImpl implements CheckoutWidgetService {
#Inject
private CheckoutWidgetSessionService sessionService;
private final List<PaymentMethod> paymentMethods = new ArrayList<>();
private final String deliveryAddress;
public CheckoutWidgetServiceImpl() {
paymentMethods.add(new PaymentMethod("PayPal", "/images/paypal-logo.png"));
paymentMethods.add(new PaymentMethod("Mastercard", "/images/mc-logo.png"));
paymentMethods.add(new PaymentMethod("Visa", "/images/visa-logo.png"));
paymentMethods.add(new PaymentMethod("Amex", "/images/amex-logo.png"));
paymentMethods.add(new PaymentMethod("Google Checkout", "/images/google-logo.png"));
deliveryAddress = "xxxxx";
}
#Override
public List<PaymentMethod> getPaymentMethods() {
System.out.println("Returning paymentMethods: " + paymentMethods);
return paymentMethods;
}
#Override
public CardDetails getCardDetails(String name) {
CardDetails cardDetails = new CardDetails();
cardDetails.setCardType(name);
return cardDetails;
}
#Override
public CardDetails buildCardDetails(LocalParameterMap params) {
CardDetails cardDetails = new CardDetails();
cardDetails.setCardNumber(params.get("cardNumber"));
cardDetails.setExpiryMonth(params.get("expiryMonth"));
cardDetails.setExpiryYear(params.get("expiryYear"));
cardDetails.setNameOnCard(params.get("nameOnCard"));
cardDetails.setCvv2(params.get("cvv2"));
return cardDetails;
}
#Override
public String getDeliveryAddress() {
return deliveryAddress;
}
#Override
public void invalidateSession(HttpSession session) {
session.invalidate();
}
private RedirectUrls getRedirectUrls(String sessionId) {
CheckoutWidgetSession widgetSession = sessionService.getCheckoutWidgetSession(sessionId).get();
return widgetSession.getRedirectUrls();
}
#Override
public void completeCheckout(HttpSession session, RequestContext context, CardDetails cardDetails) {
RedirectUrls redirects = getRedirectUrls(session.getId());
context.getFlowScope().remove("paymentMethods");
UriBuilder uriBuilder = UriBuilder.fromUri(URI.create(redirects.getSuccessUrl()));
String forwardUrl = uriBuilder.queryParam("transactionId", "12345").toString();
context.getFlowScope().put("forwardUrl", forwardUrl);
context.getFlowScope().put("target", "_top");
}
#Override
public void cancelCheckout(HttpSession session, RequestContext context) {
RedirectUrls redirects = getRedirectUrls(session.getId());
context.getFlowScope().remove("paymentMethods");
String forwardUrl = redirects.getCancelUrl();
context.getFlowScope().put("forwardUrl", forwardUrl);
context.getFlowScope().put("target", "_top");
}
}
Application.java
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.pay.widgets.checkout"})
#Import(JerseyAutoConfiguration.class)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Okay this was a really stupid mistake on my part, the problem turned out to be down to a typo in the #RequestParameter annotation on the Controller:
payments/checkout-widgets
Which didn't line up with what was in the WebflowConfig defined flowRegistry:
payments/checkout-widget
I can only assume the resource was cached by Tomcat which is why it took so long for the issue to manifest and threw me off the scent in terms of suspecting my own changes to be responsible.

Resources