How to use Spring AOP aspects with Groovy and Grails, specific caching example - caching

We built a large insurance policy and claim management system using Grails and Groovy. Performance problems are slowing down the site because all 'READS' fetch from the database, which is not necessary since most data is static. We want to introduce a simple key/value cache in the Grails layer, but we don't want to litter the existing code with cache.get() and cache.set() code, we want to use aspects instead.
Here is a sample from our main controller....
InsuranceMainController {
def customer {
//handles all URI mappings for /customer/customerId
}
def policy {
//handles all URI mappings for /policy/policyId,
}
def claim {
//handles all URL mappings for /claim/claimId
}
As far as the cache goes, assume for the moment it's a simple Map named "cache" that's available as a globally-scoped object, and objects in the cache are keyed by request URI...
cache.put("/customer/99876", customerObject)
cache.put("/policy/99-33-ARYT", policyObject)
Going back to the controller, if we just litter the code with cache.get()/set(), which is what we want to avoid using Spring AOP, we'll end up with messy code. We want to achieve the following functionality with apsects, or with just a simpler and cleaner implementation...
InsuranceMainController {
def customer {
Object customer = cache.get(request.getRequestURI())
if ( customer != null)
//render response with customer object
}else
//get the customer from the database, then add to cache
CustomerPersistenceManager customerPM = ...
customer = customerPM.getCustomer(customerId)
cache.put(request.getRequestURI(), customer)
}
}
We need examples that show how we can achieve the above functionality using Spring AOP or something simpler in Grails while avoiding the littering of the code with cache.get()/set(). Suggestions to refactor the existing controller are welcome if it's required to get AOP working properly.
Thanks in advance

Rather than using AOP, you could adapt Mr Paul Woods' controller simplification pattern to move the cache handling out to a single method?
Something like this might work:
class InsuranceMainController {
def customer = {
Object customer = withCachedRef( 'customerId' ) { customerId ->
CustomerPersistenceManager customerPM = ...
customerPM.getCustomer(customerId)
}
}
def policy = {
//handles all URI mappings for /policy/policyId,
Object policy = withCachedRef( 'policyId' ) { policyId ->
PolicyPersistenceManager policyPM = ...
policyPM.getPolicy(policyId)
}
}
// ...
private def withCachedRef( String id, Closure c ) {
Object ret = cache.get( request.requestURI )
if( !ret ) {
ret = c.call( params[ id ] )
cache.put( request.requestURI, ret )
}
ret
}
}
However, I haven't tested it at all :-( Just a suggestion of an alternative to AOP

Related

Get S3 Objects With Apache Camel

I am trying to expose a rest endpoint with camel. It will show a json data which is inside some .json files stored in s3 bucket. Also, it will filter by a date range.
First, I got some s3 objects informations in my Camel routes. (I am using kotlin)
//expose the endpoint
from("jetty:http://0.0.0.0:8080/getObjects")
.routeId("list-objects-on-bucket")
.to("aws-s3://[bucket-name]?amazonS3Client=#s3Client&operation=listObjects")
.process(ListObjects())
.to("direct:filter-list-from-s3")
then, I filter the data. (Till here everything is alright)
from("direct:filter-list-from-s3")
.routeId("filter-list-from-s3")
.process(FilterObjects())
.to("log:info")
But in my FilterObject class I do not know how to download every files that matches (look the if statement) and pass it to the next route that will treat them
class SaoMateusFilterObjects : Processor {
override fun process(exchange: Exchange?) {
val start_date = exchange!!.getIn().getHeader("start_date") as String
val end_date = exchange.getIn().getHeader("end_date") as String
val formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy")
val start = LocalDate.parse(start_date).format(formatter)
val end = LocalDate.parse(end_date).format(formatter)
val objectsNames = exchange!!.getIn().body as LinkedList<String>
for (objectName in objectsNames) {
if(objectName.contains(start) && objectName.contains(end) && objectName.contains(".json")) {
exchange.getIn() to "aws-s3://[bucket-name]?amazonS3Client=#s3Client&operation=getObject&fileName=$objectName"
}
}
}
}
Some problems are:
1 - I want to read. By I think I can't use the from() method. Because it can be use just once. So, the to() method is used to read.
2 - exchange.getIn().to("[s3-uri]") maybe/must be converted in S3Object(). How??
Can Someone help me with this?
Thank you
Instead of .to route, use .bean() and use the s3.getObject method to get the S3Object.
always Prefer using .bean() over .processor().
offical_s3_java_object operation sample.

Performance in microservice-to-microservice data transfer

I have controller like this:
#RestController
#RequestMapping("/stats")
public class StatisticsController {
#Autowired
private LeadFeignClient lfc;
private List<Lead> list;
#GetMapping("/leads")
private int getCount(#RequestParam(value = "count", defaultValue = "1") int countType) {
list = lfc.getLeads(AccessToken.getToken());
if (countType == 1) {
return MainEngine.getCount(list);
} else if (countType == 2) {
return MainEngine.getCountRejected(list);
} else if (countType == 3) {
return MainEngine.getCountPortfolio(list);
} else if (countType == 4) {
return MainEngine.getCountInProgress(list);
} else if (countType == 5) {
return MainEngine.getCountForgotten(list);
} else if (countType == 6) {
return MainEngine.getCountAddedInThisMonth(list);
} else if (countType == 7) {
return MainEngine.getCountAddedInThisYear(list);
} else {
throw new RuntimeException("Wrong mapping param");
}
}
#GetMapping("/trends")
private boolean getTrend() {
return MainEngine.tendencyRising(list);
}
It is basically a microservice that will handle statistics basing on list of 'Business Leads'. FeignClient is GETting list of trimmed to the required data leads. Everything is working properly.
My only concern is about performance - all of this statistics (countTypes) are going to be presented on the landing page of webapp. If i will call them one by one, does every call will retrieve lead list again and again? Or list will be somehow stored in temporary memory? I can imagine that if list become longer, it could take a while to load them.
I've tried to call them outside this method, by #PostConstruct, to populate list at the start of service, but this solution has two major problems: authentication cannot be handled by oauth token, retrieved list will be insensitive to adding/deleting leads, cause it is loaded at the beginning only.
The list = lfc.getLeads(AccessToken.getToken()); will be called with each GET request. Either take a look at caching the responses which might be useful when you need to obtain a large volume of data often.
I'd start here: Baeldung's: Spring cache tutorial which gives you an idea about the caching. Then you can take a look at the EhCache implementation or implement own interceptor putting/getting from/to external storage such as Redis.
The caching is the only way I see to resolve this: Since the Feign client is called with a different request (based on the token) the data are not static and need to be cached.
You need to implement a caching layer to improve performance. What you can do is, you can have cache preloaded immediately after application starts. This way you will have the response ready in the cache. I would suggest to go with Redis cache. But any cache will do the job.
Also, it will be better if you can move the logic of getCount() to some service class.

Conditional get in Spring framework

I am trying to learn Scala using Spring framework. I have to implement conditional get logic in my code. I understand it could be done using etag or Last-Modified option.
Here is my piece of code:
var lastModifiedTime: Long = _;
#RequestMapping(value= Array("/users/{id}"),method=Array(RequestMethod.GET),headers = Array("Content-Type=application/json"))
#ResponseBody
def getmeth(request: User_details, web: WebRequest): User_details = {
if (web.checkNotModified(lastModifiedTime)) {
return null
} else {
lastModifiedTime = System.currentTimeMillis()
}
Could you please help me to fix this code?
Disclaimer: I don't know Spring Web.
But according to the documentation fist of all you should take action if request is modified so you should remove bang (!) from condition. Also lastModifiedTime should be computed from the outside of the getmeth method.
Notice that unlike in Java if statement is an expression and it returns value so you shouldn't use return statement.
As it was said in comment conditional code can be easy and safely done using Scala's Option. In Scala you should always avoid null, as it is hard to distinguish it from incorrect behavior of your code, and it is very easy to forget or don't know that it is required to write logic dealing with it - you must always read the javadoc (assuming it exists and it is up to date). When you use Option type compiler will force you to deal with "nullability".
def getmeth(request: User_details, web: WebRequest): Option[User_details] =
if (web.checkNotModified(lastModifiedTime)) {
None
} else {
val userDetails = yourLogic()
Some(userDetails)
}
Then you can perform an action when option is a Some instance. To do that you can use map method.
getmeth(req, web) map { userDetails =>
userDetails.getName
}
EDIT: #optimus Now when you gave wider scope I see that your method signature is forced by framework and yon can't wrap your value with Option. I think that your problem may be that you update lastModifiedTime on every request so it seems reasonable to me that checkNotModified is always false. I think that you should use that feature only on requests that not always update checkNotModified to current time. It becomes pointless otherwise.
Update lastModifiedTime once your resource has become outdated.
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = Array("/users/{user_id}"),method = Array(RequestMethod.GET))
def getUser(#PathVariable("user_id") user_id: String,
#Context req : Request ): Any = {
val u = hm.get(user_id).asInstanceOf[User]
val tag = u.hashCode().asInstanceOf[EntityTag]
if (req.getMethod().equals("GET")) {
val rb : Response.ResponseBuilder = req.evaluatePreconditions(tag);
if (rb != null)
{
rb
}
else
{
// val u = hm.get(user_id).asInstanceOf[User]
u
}
}

Best way to pass objects between controller actions in grails

I want a link to open up another view in my webapp to display information about the specified object.
What is the best way to pass objects between controllers actions in grail?
Actions can be chained using the chain controller method.
Chaining allows the model to be retained from one action to the next.
The earlier answers are incomplete. So, I am compiling them along with my inputs and making them clearer.
You have two options:
Chaining the actions:
def action1() = {
DomainClass domainInstance = DomainClass.findById(params.id);
chain (action: 'action2', model: [domainInstance: domainInstance]);
}
def action2() = {
DomainClass domainInstance = chainModel?.domainInstance ?: DomainClass.findById(params.id);
[domainInstance: domainInstance];
}
However, the successor action seems to use a fresh database session
instead of reusing that of the predecessor (which may also be
configurable in Grails, I don't know how though). So any lazily
loaded entity may not be fully loaded in the successor action and
may give LazyInitializationException (depending on your ORM configuration of course).
Forwarding the request:
def action1() = {
DomainClass domainInstance = DomainClass.findById(params.id);
forward (action: 'action2', model: [domainInstance: domainInstance]);
}
def action2() = {
DomainClass domainInstance = request?.domainInstance ?: DomainClass.findById(params.id);
[domainInstance: domainInstance];
}
Unlike in the previous case, request forwarding reuses the existing session so lazy loading issues will not occur.
As you can see, the syntax for both is almost identical. But one should prefer request forwarding as per the requirement in question due to the issue mentioned above. Another important detail is regarding the URL viewed in the address bar on/after page loading. Forwarding the requests will PRESERVE the page URL while chaining the actions will CHANGE the page URL to that of the latest action.
(Late to the party, but...) I'm using Grails 2.4.4, which allows me to do the below:
def usernameLogin() {
SecurityToken securityToken = authService.loginWithUserPass(params.user, params.pass)
chain action: 'afterLogin', model: [securityToken: securityToken]
}
def ssoLogin() {
SecurityToken securityToken = authService.ssoLogin(params.remoteUser, params.key)
chain action: 'afterLogin', model: [securityToken: securityToken]
}
def afterLogin() {
SecurityToken securityToken = (SecurityToken) chainModel['securityToken']
if (securityToken.valid) {
forward action: 'loggedInRedirect'
}
else {
forward action: 'loginFailed'
}
}
SecurityToken is an object that contains string and enum
The key is 1) using "chain action" in source action, 2) using chainModel in target action
Hope this helps.

Grails filters: Any way of chaining filters?

Is there a way to chain several filters in a grails application (as in Java filters)? Maybe something with spring?
I've written a couple of filters, and would like to get them to execute serially (order is not particularly important). The reason behind this? I need to write about 20, 30 filters, and don't want them all in the same file.
I've read about Spring's DelegatingFilterProxy but can't figure out on how to configure it to chain all my grails filters.
Since Grails 1.3.1 you can chain filters by using the "dependsOn" keyword:
def dependsOn = [MyOtherFilters.class]
http://jira.codehaus.org/browse/GRAILS-6229
I may not be understanding the core issue here, but the simple answer might be "they're already chained". Filters are executed based on the selector you put in the filter closure (e.g. myPreProcessorFilter(controller:'', action:'') {}). All selectors that match your controller/action will execute. I do this all the time with logging and performance measurement filters.
Here's an example. Both the logAction and measureMethodTime filters will be applied to all controllers and actions (since I left the selector wide open).
import org.springframework.web.context.request.RequestContextHolder as RCH
import com.x.y.*
class PerformanceFilters {
def filters = {
logAction(controller:'*', action:'*'){
before = {
log.debug("${controllerName}.${actionName}: entering; params=${params}")
}
}
measureMethodTime(controller:'*', action:'*'){
before = {
def session = RCH.currentRequestAttributes().getSession(false)
if (session)
{
Q.startTimer("${session.id}-${controllerName}-${actionName}", "method.${controllerName}.${actionName}")
}
}
afterView = {
def session = RCH.currentRequestAttributes().getSession(false)
if (session)
{
Q.stopTimer("${session.id}-${controllerName}-${actionName}", "method.${controllerName}.${actionName}")
}
}
}
}
}
http://grails.org/doc/latest/guide/single.html#6.6.4%20Filter%20Dependencies

Resources