I am trying to implement ehcache to get static data (from table) loaded during application startup however when I make a call again to database, the call is going to database (can see running sql on console) instead of taking values from ehcache.
my code is:
ehcache.xml as below:
<?xml version="1.0" encoding="UTF-8"?>
<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="ObjectList"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="2000000" timeToLiveSeconds="900000000000"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
my repository class is:
public interface customRepository extends JpaRepository<Object, Long> {
#Cacheable(value = "ObjectList", cacheManager="abclCacheManager")
public Object findById(Long id);
#Cacheable(value = "ObjectList", cacheManager="abclCacheManager")
public List<Object> findAll();
}
and my cacheInitialiser class is:
#Configuration
#EnableCaching
#ComponentScan("com.abcl.process")
public class EhCacheConfiguration {
#Bean("abclCacheManager")
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);
cmfb.setCacheManagerName("abclCacheManager");
return cmfb;
}
}
I am testing my this using below:
public class testCache {
doSomething() {
List<Object> listObject = repo.findAll();
listObject.size();
}
public void getOne() {
Object o = repo.findById(1L);
}
}
I can see a db hit in getAll method however I thought the results would get stored in cache and in the second call there would not be a db hit by method getById however I see a db hit on second call as well.
Can anyone please suggest if I am missing anything here.
When you cache the results of findAll it creates a single entry in the cache which maps the key generated by Spring caching, since your method has no parameter, to the List<Object>. It does not put into the cache one mapping per list element between id and the Object.
So when you use findById(Long), Spring caching will look for a cache entry mapping to the id. And since it cannot find one, it will hit the database.
There is no way of having Spring caching put one mapping per collection element. If that is really what you need, you will have to code it instead of relying on the #Cacheable annotation.
Related
I have a compositeItemProcessor as below
<bean id="compositeItemProcessor" class="org.springframework.batch.item.support.CompositeItemProcessor">
<property name="delegates">
<list>
<bean class="com.example.itemProcessor1"/>
<bean class="com.example.itemProcessor2"/>
<bean class="com.example.itemProcessor3"/>
<bean class="com.example.itemProcessor4"/>
</list>
</property>
</bean>
The issue i have is that within itemProcessor4 i require values from both itemProcessor1 and itemProcessor3.
I have looked at using the Step Execution Context but this does not work as this is within one step. I have also looked at using #AfterProcess within ItemProcessor1 but this does not work as it isn't called until after ItemProcessor4.
What is the correct way to share data between delegates in a compositeItemProcessor?
Is a solution of using util:map that is updated in itemProcessor1 and read in itemProcessor4 under the circumstances that the commit-interval is set to 1?
Using the step execution context won't work as it is persisted at chunk boundary, so it can't be shared between processors within the same chunk.
AfterProcess is called after the registered item processor, which is the composite processor in your case (so after ItemProcessor4). This won't work neither.
The only option left is to use some data holder object that you share between item processors.
Hope this helps.
This page seems to state that there are two types of ExecutionContexts, one at step-level, one at job-level.
https://docs.spring.io/spring-batch/trunk/reference/html/patterns.html#passingDataToFutureSteps
You should be able to get the job context and set keys on that, from the step context
I had a similar requirement in my application too. I went with creating a data transfer object ItemProcessorDto which will be shared by all the ItemProcessors. You can store data in this DTO object in first processor and all the remaining processors will get the information out of this DTO object. In addition to that any ItemProcessor could update or retrieve the data out of the DTO.
Below is a code snippet:
#Bean
public ItemProcessor1<ItemProcessorDto> itemProcessor1() {
log.info("Generating ItemProcessor1");
return new ItemProcessor1();
}
#Bean
public ItemProcessor2<ItemProcessorDto> itemProcessor2() {
log.info("Generating ItemProcessor2");
return new ItemProcessor2();
}
#Bean
public ItemProcessor3<ItemProcessorDto> itemProcessor3() {
log.info("Generating ItemProcessor3");
return new ItemProcessor3();
}
#Bean
public ItemProcessor4<ItemProcessorDto> itemProcessor4() {
log.info("Generating ItemProcessor4");
return new ItemProcessor4();
}
#Bean
#StepScope
public CompositeItemProcessor<ItemProcessorDto> compositeItemProcessor() {
log.info("Generating CompositeItemProcessor");
CompositeItemProcessor<ItemProcessorDto> compositeItemProcessor = new CompositeItemProcessor<>();
compositeItemProcessor.setDelegates(Arrays.asList(itemProcessor1(), itemProcessor2(), itemProcessor3), itemProcessor4()));
return compositeItemProcessor;
}
#Data
public class ItemProcessorDto {
private List<String> sharedData_1;
private Map<String, String> sharedData_2;
}
I start to learn Spring Cache abstraction.
I use Spring boot, Spring Data Jpa, EhCache provider for this purpose.
My ehcache.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ehcache>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache maxElementsInMemory="100"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true">
</defaultCache>
<cache name="teams"
maxElementsInMemory="500"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="100"
overflowToDisk="false">
</cache>
My service:
#CacheConfig(cacheNames = "teams")
#Service
public class TeamService {
#Autowired
private TeamRepository teamRepository;
#Cacheable
public Team findById(long id) {
return teamRepository.findById(id).get();
}
#Cacheable
public List<Team> findAll() {
return teamRepository.findAll();
}
#CachePut
public Team save(Team team) {
return teamRepository.save(team);
}
#CacheEvict
public void delete(long id) {
teamRepository.deleteById(id);
}
}
My controller:
#RestController
public class TeamController {
#Autowired
private TeamService teamService;
#GetMapping("/teams")
public List<Team> getAll() {
return teamService.findAll();
}
#GetMapping("/team/{id}")
public Team getById(#PathVariable long id) {
return teamService.findById(id);
}
#DeleteMapping("/team/{id}")
public void delete(#PathVariable long id) {
teamService.delete(id);
}
#PostMapping("/team")
public Team save(#RequestBody Team team) {
return teamService.save(team);
}
}
I am performing requests to my controller...
When I perform getAll() method of the controller data are cached correctly and then don't exucute query to database at next times. Then I update and delete data from the database using corresponding methods of my controller, which service methods are marked as #CachePut and #CacheEvict respectively and must refresh cache. Then I perform above getAll() method again and get the same response like at the first time but I want that it will be refreshed after performing delete and update requests.
What's I doing wrong or How I can get the desired result?.
When you put #Cachable annotation on a method so all entries will be kept on cache added by default a name then the first cachable is different to second cachable, so if you want to work well you need to add a name that you want, for example:
#Cachable("teams")
#Cachable("teams")
#CachePut("teams")
#CacheEvict(value="teams", allEntries=true)
You can get more information in this link: https://www.baeldung.com/spring-cache-tutorial
Perhaps a best solution would be this:
#Cachable("team")
#Cachable("teams")
#Caching(put = {
#CachePut(value="team"),
#CachePut(value="teams") })
#Caching(evict = {
#CacheEvict(value="team", allEntries=true),
#CacheEvict(value="teams", allEntries=true) })
I have a Spring REST web service which populates a generic object based on data we have in a database, the goal is to have the users pass a parameter to the web service to to indicate the format they want the output to be in. Based on their input we will use the correct JSONSerializer to give them what they want.
I have set up my webservice as follows, in my spring-ws-servlet.xml I have set our company ObjectMapper to be used by the mvc:message-converters, I have also set it on the RestController so that it can adjust the ObjectMapper to register the serializer. It looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="endpoint" class="org.company.Controller">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="org.company.CompanyObjectMapper" />
</beans>
The controller looks like this:
#RestController
public class Controller {
private ObjectMapper objectMapper;
#RequestMapping(...)
public GenericObject getObject(#PathVariables ...) {
//Get Object from database, just creating an object for example
GenericObject object = new GenericObject();
//Based on the user input we will pick out
//a Serializer that extends JsonSerializer<GenericObject>
BaseSerializer serializer = getSerializer();
//Create a simpleModule and use it to register our serializer
SimpleModule module = new SimpleModule();
module.addSerializer(GenericObject.class, serializer);
//get module and register the serializer
ObjectMapper mapper = getObjectMapper();
mapper.registerModule(module);
return object;
}
public ObjectMapper getObjectMapper() {
return objectMapper;
}
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}
The issue is that when I publish my webapp, the first query works correctly, if I specify format=format1, I will get the output in format1. However, after that I can only receive format1. I may specify format=format2, but still get the output in format1. I believe the issue is that the ObjectMapper still has the module registered to it from the first query. I have read that I can avoid this problem by creating a new ObjectMapper every time, but I am not sure how to set that to be used by Spring when it outputs the JSON.
Could someone help me come up with a solution to either create a new ObjectMapper every time I run the code and set that ObjectMapper to the Spring rest service, or help me figure out how I can "unregister" any modules that are registered on the object mapper before setting the latest desired serializer?
An idea could be to create and configure all the mappers you need at startup time as a spring beans.
Then create the default object mapper that will work as a dispatcher for other object mappers (or as the fallback one), and it may be aware of the current http request.
You can register all the mappers in this object mapper, register this mapper to be used as the default one in spring.
Something like this maybe :
public class RequestAwareObjectMapper extends ObjectMapper{
private Map<String, ObjectMapper > mappers = new HashMap<>();
#Override
public String writeValueAsString(Object value) throws JsonProcessingException{
HttpServletRequest req = null;//get request from spring context, if any, this is a managed spring bean it wont be a prorblem
String param = null; // read the param from the query
ObjectMapper mapper = mappers.get(param);
if(mapper == null){
mapper = this;
}
return mapper.writeValueAsString(value);
}
public void registerMapper(String key, ObjectMapper mapper){...}
}
in this way you are not going to pollute your controller with references to the object mapper and you can carry on using #ResponseBody (thanks to #RestController)..
I am sure there's a cleaner way to achieve the same result integrating a similar solution in the spring flow, can't look on something better right now.
Create your customObjectMapper class and auto wire it to your controller using #Autowire annotation. You can then create different methods to create different formatted objects.
You can also send serialiser as parameters.
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
super();
super.setSerializationInclusion(JsonInclude.Include.ALWAYS);
super.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
..... etc.....
super.setDateFormat(df);
}
public byte[] generateJsonFormat1(Object value, BaseSerializer serializer) throws IOException, JsonGenerationException, JsonMappingException {
Hibernate4Module hm = new Hibernate4Module();
hm.configure(Hibernate4Module.Feature.USE_TRANSIENT_ANNOTATION, false);
hm.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING, false);
.....
.....
hm.addSerializer(Object.class, serializer);
return super.registerModule(hm).writeValueAsBytes(value);
}
public byte[] generateJsonFormat2(Object value, BaseSerializer serialiser) throws IOException, JsonGe nerationException, JsonMappingException {
SimpleModule sm = new SimpleModule();
sm.addSerializer(Object.class, serialiser);
return super.registerModule(hm).writeValueAsBytes(value);
}
}
Above code is a snippet from my own application. I hope it gives the idea.
I'm doing a login. The problem is: my isUserLoggedIn() method is called several times by other sessions (i've checked using
(HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false)).
The Login bean (of a JSF page) is this:
#Named
#SessionScoped
public class Login implements Serializable {
#Inject
private Credentials credentials;
private UserData user;
public String login() {
if (this.credentials.getUsername().equals("daniel")) {
user = new UserData("Daniel");
return "success";
}
return "failure";
}
public boolean isUserLoggedIn() {
return user != null;
}
public String logout() {
user = null;
((HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false)).invalidate();
return "success";
}
public String getUsername() {
return getUser() == null ? "" : getUser().getUsername();
}
#Produces
public UserData getUser() {
return user;
}
}
So, what happens is: when login() is called, I can see via getSession() that it is X, but then, afterwards while trying to access another page, when calling isUserLoggedIn(), the getSession() method returns Y instead of X, and the user attribute is null. Frequently the isUserLoggedIn() method is called several times with just 1 request, and it's session changes each time it is called.
By the way, I'm using JBoss AS7 Final, and my faces-config.xml is the following:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<navigation-rule>
<from-view-id>/login.xhtml</from-view-id>
<navigation-case>
<from-action>#{login.login}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/secured/home.xhtml</to-view-id>
<redirect />
</navigation-case>
<navigation-case>
<from-action>#{login.login}</from-action>
<from-outcome>failure</from-outcome>
<to-view-id>/login.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/*.xhtml</from-view-id>
<navigation-case>
<from-action>#{login.logout}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/login.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
</faces-config>
Any ideas? Thank you.
After some time back there I discovered that the problem had to do with the url path. The cookie was generated for a path and when changing the path and trying to access the session, it was generated another one.
Anyway, I discovered that this is definitely NOT the way to secure Java EE apps (see Java EE 6 manual), so I'm going other way.
Thanks.
Here is my service layer:
#Service
#RemoteProxy
public class ReturnToDWR{
#Autowired
private DAOLayer daoLayer;
#RemoteMethod
public List<String> returnRecord(String id){
List<String> list = daoLayer.returnPendingRecords(id);
return list;
}
}
DWR configuratin setting in applicationContext.xml file:
<dwr:configuration />
<dwr:controller id="dwrController" debug="true" />
<bean id="returnToDWR" class="com.service.ReturnToDWR">
<dwr:remote javascript="returnToDWR">
<dwr:include method="returnRecord" />
</dwr:remote>
</bean>
When i am calling returnRecord() from my Controller, it is working. But when i am calling same method from jsp using DWR it shows me NullPointerException on List<String> list = daoLayer.returnPendingRecords(id); line.
I think spring is unable to autowire private DAOLayer daoLayer; in the case of DWR.
Please tell me how can i fix my code to work with DWR?
Thanks
Shams
When you place a brakepoint on that line, can you see in the debugger, that value of daoLayer is really null? Isn't it possible that the id value is null and that causes that NullPointerException inside the returnPendingRecords method?