#Cacheable : how to pass a new attribute that can be used in my own KeyGenerator? - spring

I need your expertise :)
I'm working on a application where method calls on a service need to be authenticated.
That means I want each method call to be cached with a key containing the username (to avoid for an unauthorized user to retrieve information cached by an authorized one).
With a personnalized KeyGenerator, all works fine.
Example of my key : username:USERNAME.appVersion:VERSION.METHOD.PARAM1.etc
But at some location, I got methods that retrieve a national content : this one will be the same for each user. And I want to avoid a cache key for each user asking for this content.
Example : appVersion:VERSION.METHOD.PARAM1.etc
So when I'm positioning my #Cacheable annotations, is there any way to set a new parameter in it ? The Key Generator will be able to catch it and know if he had to prefix the cache key name with user information or not.
Thanks for your help :)
Take care

I don't really understand what you're saying by "set a new parameter in it". That parameter should come from somewhere right?
KeyGenerator gives you access to the Method, the actual instance and the method arguments. You may want to have a specific KeyGenerator for this particular cache operation which is something that will be available as from Spring 4.1 but in the mean time you can implement a composite that invokes the right KeyGenerator instance based on the method or, for instance, an annotation you have created to flag it.

Thank you snicoll, that was crystal clear and you really helped me a lot :)
Waiting for Spring 4.1, my team and I decided to use a custom #SharedCache annotation.
Here is some code samples to help if someone is in the same situation.
Given an existing custom GenericKeyGenerator (he's building a custom cache key for each cached method invocation)
We have a new custom AuthenticatedGenericKeyGenerator : he's inherited from GenericKeyGenerator and simply prefixing the cache key with user information
The application is now using AuthenticatedGenericKeyGenerator by default :
<cache:annotation-driven key-generator="keyGenerator"/>
<bean id="keyGenerator" class="your.package.AuthenticatedGenericKeyGenerator" />
AuthenticatedGenericKeyGenerator.java in details :
public class AuthenticatedGenericKeyGenerator extends GenericKeyGenerator {
public AuthenticatedGenericKeyGenerator() {
super(...);
}
#Override
public Object generate(final Object target, final Method method, final Object... params) {
String cacheKey = super.generate(target, method, params).toString();
if(!method.isAnnotationPresent(SharedCache.class)) {
cacheKey = "user:" + some user information + "." + cacheKey;
}
return cacheKey;
}
}
Our custom #SharedCache annotation :
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface SharedCache {
}
Now we just have to annotate #Cacheable methods with an extra #SharedCache if we want the cache key to be shared and not be unique (with an user id for example).

Related

Cache key issues with Jcache

I am using JSR107 caching with Springboot. I have following method.
#CacheResult(cacheName = "books.byidAndCat")
public List<Book> getAllBooks(#CacheKey final String bookId, #CacheKey final BookCategory bookCat) {
return <<Make API calls and get actual books>>
}
First time it makes actual API calls, and second time it loads cache without issue. I can see the following part of log.
Computed cache key SimpleKey [cb5bf774-24b4-41e5-b45c-2dd377493445,LT] for operation CacheResultOperation[CacheMethodDetails ...
But the problem is I want to load cache without making even first API call, Simply needs to fill the cache like below.
String cacheKey = SimpleKeyGenerator.generateKey(bookId, bookCategory).toString();
cacheManager.getCache("books.byidAndCat").put(cacheKey, deviceList);
When I am checking, hashcode of cachekeys are same in both cases, But it is making API calls. If the hashcode is same in both cases, why it is making API calls without considering the cache ?
When debugging spring classes identified that, org.springframework.cache.interceptor.SimpleKeyGenerator is used with the cache key generation even #CacheResult is there.
EDIT and enhance the question :
Apart from that if getAllBooks has overloaded methods, and then call this cached method via separate overloaded method, in that case also method caching is not working.
I'm not an expert of JSR107 annotations in the context of Spring. I use the Spring Cache annotations instead.
When using JSR107, the key used is a GeneratedCacheKey. So that's what you should put in your cache. Not the toString() of it. Note that SimpleKeyGenerator isn't returning a GeneratedCacheKey. It returns a SimpleKey which is the key used by Spring when using its own cache annotations instead of JSR-107. For JSR-107, you need a SimpleGeneratedCacheKey.
Then, if you want to preload the cache, just call getAllBooks before needing it.
If you want to preload the cache in some other way, a #javax.cache.annotation.CachePut should do the trick. See its javadoc for an example.
As #Henri suggested, we can use the cacheput. But for that we need methods. With the below we can update the cache very similar to the cacheput,
//overloaded method both id and cat is available.
List<Object> bookIdCatCache = new ArrayList<>();
bookIdCatCache.add(bookId);
bookIdCatCache.add(deviceCat);
Object bookIdCatCacheKey = SimpleKeyGenerator.generateKey(bookIdCatCache.toArray(new Object[bookIdCatCache.size()]));
cacheManager.getCache("books.byidAndCat").put(bookIdCatCacheKey , bookListWithIdAndCat);
//overloaded method only id is there
List<Object> bookIdCache = new ArrayList<>();
String nullKey = null
bookIdCache.add(bookId);
bookIdCache.add(nullKey);
Object bookIdCacheKey = SimpleKeyGenerator.generateKey(bookIdCache.toArray(new Object[bookIdCache.size()]));
cacheManager.getCache("books.byidAndCat").put(bookIdCacheKey , bookListWithId);
//Not correct(My previous implementation)
String cacheKey = SimpleKeyGenerator.generateKey(bookId, bookCategory).toString();
//Correct(This is getting from spring)
Object cacheKey = SimpleKeyGenerator.generateKey(bookIdCatCache.toArray(new Object[bookIdCatCache.size()]));

How to correctly initialize an object that have to contain the data retrieved by 2 methods of my controller in this Spring MVC application?

I am pretty new in Spring MVC and I have the following doubt about how correctly achieve the following task.
I am working on a web application that implement a user registration process. This registration process is divided into some consecutive steps.
For example in the first step the user have to insert a identification code (it is a code that identify uniquely a user on some statal administration systems) and in the second step it have to compile a form for his personal data (name, surname, birth date, and so on).
So, actually I have the following controller class that handle these steps:
#Controller
public class RegistrazioneController {
#Autowired
private LoadPlacesService loadPlacesService;
#RequestMapping(value = "/iscrizioneStep1")
public String iscrizioneStep1(Model model) {
return "iscrizioneStep1";
}
#RequestMapping(value = "/iscrizioneStep2", method=RequestMethod.POST)
public String iscrizioneStep2(Model model, HttpServletRequest request, #RequestParam("cf") String codicFiscale) {
System.out.println("INTO iscrizioneStep2()");
//String codicFiscale = request.getParameter("cf");
System.out.println("CODICE FISCALE: " + codicFiscale);
model.addAttribute("codicFiscale", codicFiscale);
return "iscrizioneStep2";
}
#RequestMapping(value = "/iscrizioneStep3", method=RequestMethod.POST)
public String iscrizioneStep3(#ModelAttribute("SpringWeb")Step2FormCommand step2Form, ModelMap model, HttpServletRequest request) {
System.out.println("INTO iscrizioneStep3()");
System.out.println("NOME: " + step2FormCommand.getName());
return "iscrizioneStep3";
}
Into the iscrizioneStep2() it is retrieved the first code (#RequestParam("cf") String codicFiscale).
Into the iscrizioneStep3() it is retrieved a command object containing the data inserted into the form of the view in which this form was submitted, this one:
#ModelAttribute("SpringWeb")Step2FormCommand step2FormCommand
It works fine.
Now my problem is that I have another object named Step3View that have to be initialized with the aggregation of the #RequestParam("cf") String codicFiscale object retrieved into the iscrizioneStep2() method and the #ModelAttribute("SpringWeb")Step2FormCommand step2FormCommand retrieved into the iscrizioneStep3() method.
This Step3View class simply contain the String codicFiscale and all the fields of the Step2FormCommand class.
Now my doubts are: what is the best way to handle this situation? Where have I to declare this Step3View object? at controller level? (so I can use it in all my controller methods?). Have I to annotate this class with #Component (or something like this) to inject it in my controller?
What is the best solution for this situation?
I think in order to get an answer you need to understand the question and ask the right question. I think your question is "how do I pass a parameter from one page to another page in SpringMVC?". You specifically want to know how to pass the "cf" param, but readers here will tend to pass over questions that are too specific because it takes too much time to figure out what you want.
In answer to that, see Spring MVC - passing variables from one page to anther as a possible help.
Also, there are many good answers about this question for JSP in general, which can be worked into the SpringMVC architecture. See How to pass value from one jsp to another jsp page? as a possible help.

EF4.3 dependency injection when creating new objects yourself

I'm building an MVC3 application using the Entity Framework. In the application my controllers talk to a service layer. One of the controllers is a TournamentController which uses the TournamentService. This service has some standard methods such as CreateTournament, UpdateTournament etc.
When I want to insert a new tournament I want the view to have a dropdownlist of possible sports for which the tournament can be organised. So in the TournamentController's Create method I fill the ViewBag.Sports with a list of possible sports. Now to obtain this list of sports I use _tournamentService.GetAllSports(). In this GetAllSports() method of the TournamentService I want to create an instance of the SportService so I can 'forward' the question to the right service.
All services use dependency injection in the constructor to inject their own repository, like so:
private ITournamentRepository _repo;
public TournamentService(ITournamentRepository repo) {
_repo = repo;
}
My GetAllSports() method in the TournamentService looks like this:
public IEnumerable<Sport> GetAllSports() {
ISportService sportService = new SportService();
return sportService.GetSports();
}
The problem is that by calling the new SportService() it expects me to hand it an ISportRepository like in the TournamentService, where ninject creates the TournamentRepository. Now I could do the following:
public IEnumerable<Sport> GetAllSports() {
ISportService sportService = new SportService(new SportRepository());
return sportService.GetSports();
}
But the problem with that is that each repository expects an IContext, which is normally handled by ninject as well. Furthermore, I don't want two separate contexts to be instantiated.
A possible solution I found myself is to do this:
private ITournamentRepository _repo;
private ISportService _sportService;
public TournamentService(ITournamentRepository repo, ISportService sportService) {
_repo = repo;
_sportService = sportService
}
But there's only one method in my TournamentService class that would actually use the _sportService so I figure this is a bit overkill to make it a class attribute.
Keep it simple, inject ISportService to your controller and call sportService.GetSports() directly from the controller!
Your last solution valid. Inject the necessary service as part of the constructor.
And if you're worried about multiple contexts, don't let two separate contexts be created then.
In your Ninject binding:
Bind<ITournamentRepository>().To<TournamentRepository>().InRequestScope();
See https://github.com/ninject/Ninject.Web.Common/wiki/InRequestScope
The last piece is the important part. It will only create one instance of TournamentRepository for the current request. Any other requesters of TournamentRepository will get this instance.
If you're already doing this, you're set, otherwise, just add InRequestScope and you're done. (keeping in mind you'll need a reference to Ninject.Web.Common)
Hope that helps.
EDIT:
Remo is correct, I wouldn't call this from a service either. Just call for your lookup data from the controller and populate your view model. The InRequestScope advice still holds.

The difference between object validation and persistence validation in DDD?

Right now, I have a domain entity named StyleBundle. This StyleBundle takes a list of Styles:
public class StyleBundle
{
public StyleBundle(List<Style> styles)
{
this.Styles = styles;
}
public IEnumerable<Style> Styles { get; private set;}
}
So, in my original design, a StyleBundle should never be created with an empty Style list. This was a rule that the domain experts basically said was good.
I wrote this using a guard clause in the constructor:
if (styles.Count() == 0)
throw new Exception("You must have at least one Style in a StyleBundle.");
which made sure I could not create StyleBundle in an invalid state. I thought an exception made sense here b/c a StyleBundle being created without at least one Style was exceptional in the system.
Of course, change came down the road during the rest of the project, and now it should be possible for a user to create a StyleBundle without Styles, but they should not be allowed to PERSIST a StyleBundle without Styles.
So now I'm looking at my guard clause and realizing that I can't have the exception thrown from the constructor anymore.
Moving forward, I have a Service/Application layer that my code-behinds interact with when they're working with StyleBundles. In my Service Layer, I have a StyleBundleService class, and that class exposes basic functionality to the UI... among them is "CreateStyleBundle".
It seems as if I'll have to have my Service Layer check to see if the StyleBundle does or does not have any Styles before it's persisted to the database, but something about this decision feels "wrong" to me.
Anyone run into a similar thing? Basically, the different between the state of an object being valid when "new'ed up" vs. the state of the same object when it comes to persistence?
Thanks!
Mike
I would add an IsValid method to your entity. This would check if the entity is currently in a valid state (in your case, check if there are styles).
This method can be called from your Repository to check if an entity may be persisted. You can add more rules to the IsValid method for specific entities and you can implement something like a collection of Validation errors is you want to throw a meaningful exception.
Expanding what Wouter said, plus handy BeforeSaving and BeforeDeleting methods:
public interface IDomainObject<T>
{
bool IsValid();
}
public interface IEntity<T> : IDomainObject<T>
{
}
public interface IAggregateRoot<T> : IEntity<T>
{
void BeforeSaving();
void BeforeDeleting();
}
public interface IAggregateRoot { //or simply IEntity depending on the model
bool IsValid();
}
public class StyleBundle : IAggregateRoot<T> {
return styles.Count() > 0
}
public class StyleBundleRepository : Repository<StyleBundle> {
}
public abstract class Repository<T> : IRepository<T> where T : class, IAggregateRoot<T> {
public T Save(T t)
{
t.BeforeSaving(); //for all AggregateRoots, maybe logging what the aggregate was like before the changes
if(!t.IsValid())
throw Exeception("Entity invalid");
EntityStore.Current.SaveChanges();
// "AfterSaving" here, i.e.: log how the entity looks after the update
}
}
Edit: I dont personally use the IsValid idea, I go with a full class of EntityValidationErrors where I can report back to the client what was wrong before attempting to save, things that shouldnt be null, shouldnt be empty (like your Styles etc)
There are multiple strategies:
Some developers prefer to create 2 methods in the entity itself, one called IsValid() which validates the entity in terms of business rules (general validation) and another one called IsValidForPersistence() which validates the entity for persistence.
Regarding IsValid() I prefer instead not to allow invalid state in the first place by validating all inputs, and to support invariants I use factory or builder.
you may check the link http://www.codethinked.com/thoughts-on-domain-validation-part-1
for some thoughts.
I know, this question is three years old, but seeing the current answer is something I like to respond to. We are talking about the domain data. Hence, there can't be a valid StyleBundle with 0 objects. I imagine, you have a frontend editor somewhere, were you create a "new" StyleBundle and have to add at least one style, before hitting the "save" button.
At this point in the frontend, you won't have a domain object. You may have a data transfer object, that will be send with a "CreateNewStyleBundle" command.
In my opinion, the domain object must be agnostic to persitance and should always be in a valid state. If you have to call a "IsValid" method, you circumvent the whole idea of having domain objects in the first place.
That's just my humble opinion.

In Spring MVC 3, how do I bind an object to a query string when the query string parameters don't match up with the object fields?

A 3rd party is sending me part of the data to fill in my domain object via a query string. I need to partially fill in my domain object, and then have the user fill in the rest via a form. I don't have any control over the query string parameters coming in, so I can't change those, but I'd really like to be able to use Spring MVC's data binding abilities, rather than doing it by hand.
How can I do this?
To add some complication to this, some of the parameters will require extensive processing because they map to other objects (such as mapping to a user from just a name) that may not even exist yet and will need to be created. This aspect, I assume, can be handled using property editors. If I run into trouble with this, I will ask another question.
Once I have a partially filled domain object, passing it on to the edit view, etc. is no problem, but I don't know how to properly deal with the initial domain object population.
The only thing I have been able to come up with so far is to have an extra class that has it's properties named to match the inbound query parameters and a function to convert from this intermediary class to my domain class.
This seems like a lot of overhead though just to map between variable names.
Can you not just have the getter named differently from the setter, or have 2 getters and 2 setters if necessary?
private int spn;
// Standard getter/setter
public int getSpn() {
return spn;
}
public void setSpn(int spn) {
this.spn = spn;
}
// More descriptively named getter/setter
public int getShortParameterName() {
return spn;
}
public void setShortParameterName(int spn) {
this.spn = spn;
}
Maybe that is not standard bean convention, but surely would work?

Resources