Grails & AspectJ: advice for private methods is not working - grails

I need to intercept calls to private methods in Grails services. The following aspect IS working for any annotated public methods, however nothing happens when the annotation is at PRIVATE methods.
import exceptions.DwcpExeption
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
#Aspect
#Component
public class LoggerInterceptor {
private static Logger log = LoggerFactory.getLogger(LoggerInterceptor.class);
#Around("#annotation(newAnnotation)")
public Object aroundEvents(ProceedingJoinPoint proceedingJoinPoint, NewAnnotation newAnnotation) {
log.info newAnnotation.value()
String logMessage = String.format("%s.%s(%s)",
proceedingJoinPoint.getTarget().getClass().getName(),
proceedingJoinPoint.getSignature().getName(),
Arrays.toString(proceedingJoinPoint.getArgs()));
log.info "*Entering $logMessage"
def result
try {
result = proceedingJoinPoint.proceed()
catch (ex) {
log.error '', ex
}
log.info "*Exiting $logMessage. Result: $result"
return result
}
}
Maybe the problem is in config? I've tried in applicationContext.xml
<aop:aspectj-autoproxy proxy-target-class="true"/>
and in resources.groovy
aop.config("proxy-target-class": true)
Nevertheless, only public methods are intercepted.

Spring AOP is a proxy-based "AOP lite" approach in comparison to AspectJ. It only works for Spring components and only for public, non-static methods. This is also explained in the Spring AOP documentation as follows:
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
Bottom line: Please switch to AspectJ which can be easily integrated into Spring applications via LTW (load-time weaving) as described in Section 9.8, “Using AspectJ with Spring applications”.

If you don't specify the scope it defaults to public. Add a pointcut for private methods:
#Around("#annotation(newAnnotation) && execution(private * *(..))")

Related

Grails Data Service Cannot Use Regular Service

Happy Another Covid Day. When I use generate-all, Grails creates the Data Service for me. I begin to understand what a data service is.
I also have my own service for my Author and Book classes to use. I name my service ImportService. I have methods in the ImportService to clean up my book data read from a CSV file before the Data Service saves my books to the database. I also follow the instruction to make the Data Service an Abstract Class. So, I can put my own method in the Data Service.
Since the Author has its own AuthorService, and the Book has its own BookService, I want the different Data Service to access the method in my ImportService. So, I don't have to copy and paste the import CSV code multiple times. So, I put the line ImportService importService in the AuthorServie class and the BookService class. That does not go well. importService is always NULL inside the Data Service classes. I google the problem. They say I cannot inject another service to the grails.gorm.services.Service.
There is a post that says to make a bean. I am new to Grails. I have no idea what they are talking about even with the codes posted. Part of my background is Assembly Language, C, and Pascal. My head is filled with lingo like Top Down, Subroutine, library, Address, and Pointer. I have no idea what a Bean is.
This is what it is. I am wondering whether this is a bug or by design that you cannot inject a service to the gorm service.
Thanks for your "Pointer".
See the project at https://github.com/jeffbrown/tom6502servicedi. That project uses Grails 4.0.3 and GORM 7.0.7.
https://github.com/jeffbrown/tom6502servicedi/blob/main/grails-app/services/tom6502servicedi/ImportService.groovy
package tom6502servicedi
class ImportService {
int getSomeNumber() {
42
}
}
https://github.com/jeffbrown/tom6502servicedi/blob/917c51ee173e7bb6844ca7d40ced5afbb8d9063f/grails-app/services/tom6502servicedi/AuthorService.groovy
package tom6502servicedi
import grails.gorm.services.Service
import org.springframework.beans.factory.annotation.Autowired
#Service(Author)
abstract class AuthorService {
#Autowired
ImportService importService
// ...
int getSomeNumberFromImportService() {
importService.someNumber
}
}
https://github.com/jeffbrown/tom6502servicedi/blob/917c51ee173e7bb6844ca7d40ced5afbb8d9063f/grails-app/controllers/tom6502servicedi/AuthorController.groovy
package tom6502servicedi
import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
class AuthorController {
AuthorService authorService
// ...
def someNumber() {
render "The Number Is ${authorService.someNumberFromImportService}"
}
}
Sending a request to that someNumber action will verify that the ImportService is injected into the AuthorService and the AuthorService is injected into the AuthorController.
$ curl http://localhost:8080/author/someNumber
The Number Is 42

Configure Dropwizard ObjectMapper for configuration to ignore unknown

With an ObjectMapper (com.fasterxml.jackson.databind) it's possible to specify that it should ignore unknown properties. This can either be done by adding #JsonIgnoreProperties(ignoreUnknown = true) on a class level or by setting it as default behavior in the mapper. However, when doing this in the initialize() method of Application<MyConfiguration> it doesn't seem to have an effect.
ObjectMapper mapper = bootstrap.getObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
It still fails for unknown properties in the configuration file. How does one configure Dropwizard to ignore unknown properties?
The reason configuring DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES for bootstrap.getObjectMapper() doesn't have the desired effect is that ConfigurationFactory (the class later on used to parse the configuration) is enabling that particular feature of the object mapper in its constructor (see here):
public ConfigurationFactory(Class<T> klass,
Validator validator,
ObjectMapper objectMapper,
String propertyPrefix) {
...
this.mapper = objectMapper.copy();
mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
...
}
It's not possible to change the behaviour of ConfigurationFactory directly but Dropwizard provides means to override the factory that creates it, ConfigurationFactoryFactory, via Boostrap.setConfigurationFactoryFactory(). This allows to replace the real ObjectMapper with a proxy that doesn't allow to override the configuration and pass it to ConfigurationFactory:
bootstrap.setConfigurationFactoryFactory(
(klass, validator, objectMapper, propertyPrefix) -> {
return new ConfigurationFactory<>(klass, validator,
new ObjectMapperProxy(objectMapper), propertyPrefix);
}
);
The code for ObjectMapperProxy that ignores attempts to enable FAIL_ON_UNKNOWN_PROPERTIES it below:
private static class ObjectMapperProxy extends ObjectMapper {
private ObjectMapperProxy(ObjectMapper objectMapper) {
super(objectMapper);
}
private ObjectMapperProxy(ObjectMapperProxy proxy) {
super(proxy);
}
#Override
public ObjectMapper enable(DeserializationFeature feature) {
// do not allow Dropwizard to enable the feature
if (!feature.equals(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
super.enable(feature);
}
return this;
}
#Override
public ObjectMapper copy() {
return new ObjectMapperProxy(this);
}
}
Note that besides overriding enable to skip FAIL_ON_UNKNOWN_PROPERTIES copy is also implemented (together with an additional constructor) as ConfigurationFactory requires the object mapper to support copying.
While the solution above works it's obviously a workaround and I suggest upgrading to a newer Dropwizard version instead. The new Dropwizard makes ObjectMapper configuration easier to override (e.g. see this Dropwizard commit available in Dropwizard 1.1.x).
You need to disable that feature with:
bootstrap.getObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
UPDATE: The feature disable works for the API resources but not for the configuration YAML. Instead, you need to add the annotation below (same as mentioned on the question) to the configuration class:
#JsonIgnoreProperties(ignoreUnknown = true)

Guice Binding from Consumer Package

I am newbie for Guice and seeking help for the following use case :
I have developed one package say (PCKG) where the entry class of that package depends on other class like:
A : Entry point class --> #Inject A(B b) {}
B in turn is dependent on C and D like --> #Inject B(C c, D d) {}
In my binding module I am doing :
bind(BInterface).to(Bimpl);
bind(CInterface).to(CImpl);
...
Note I am not providing binding information for A as i want to provide its binding by its consumer class. (this is how the design is so my request is to keep the discussion on main problem rather than design).
Now my consumer class is doing like:
AModule extends PrivateModule {
protected void configure() {
bind(AInterface.class).annotatedWith(AImpl.class);
}
}
Also in my consumer package:
.(new PCKGModule(), new AModule())
Q1. Am i doing the bindings correctly in consumer class. I am confused because when i am doing some internal testing as below in my consumer package:
class testModule {
bind(BInterface).to(Bimpl);
bind(CInterface).to(CImpl)...
}
class TestApp {
public static void main(..) {
Guice.createInstance(new testModule());
Injector inj = Guice.createInstance(new AModule());
A obj = inj.getInstance(A.class);
}
}
It is throwing Guice creation exception.Please help me get rid of this situation.
Also one of my friend who is also naive to Guice was suggesting that I need to create B's instance in AModule using Provides annotation. But i really didn't get his point.
Your main method should look like this:
class TestApp {
public static void main(..) {
Injector injector = Guice.createInjector(new TestModule(), new AModule());
A obj = injector.getInstance(A.class);
}
Note that the Java convention is for class names to have the first letter capitalised.
I'm pretty sure your implementation of AModule isn't doing what you think it's doing either, but it's hard to be certain based on the information you've provided. Most likely, you mean to do this:
bind(AInterface.class).to(AImpl.class)`
There's no need to do anything "special" with A's binding. Guice resolves all the recursion for you. That's part of its "magic".
annotatedWith() is used together with to() or toInstance(), like this:
bind(AInterface.class).to(AImpl.class).annotatedWIth(Foo.class);
bind(AInterface.class).to(ZImpl.class).annotatedWIth(Bar.class);
Then you can inject different implementations by annotating your injection points, e.g.:
#Inject
MyInjectionPoint(#Foo AInterface getsAImpl, #Bar AInterface getsZImpl) {
....
}
It's worth also pointing out that you can potentially save yourself some boilerplate by not bothering with the binding modules (depending how your code is arranged) and using JIT bindings:
#ImplementedBy(AImpl.class)
public interface AInterface {
....
}
These effectively act as "defaults" which are overridden by explicit bindings, if they exist.

Google guice, override configuration

I'm working with Guice and have one design question. My App consists of few module:
myapp-persistence (JPA Entities, DAO, other DB related stuff)
myapp-backend (Some background daemons, they use myapp-persistence )
myapp-rest (REST app that depends on myapp-persistence)
myapp-persistence must have singleton HibernateSessionFactory. It's by Hibernate design.
No problem I can solve it with Guice:
class MyAppPersistenceModule extends AbstractModule {
override def configure(): Unit = {
bind(classOf[SomeStuff])
bind(classOf[ClientDao])
bind(classOf[CustomerDao])
bind(classOf[SessionFactory]).toProvider(classOf[HibernateSessionFactoryProvider]).asEagerSingleton()
}
#Provides
def provideDatabaseConnectionConfiguration: DatabaseConnectionConfiguration = {
DatabaseConnectionConfiguration.fromSysEnv
}
}
The problem with passing DatabaseConnectionConfiguration to that singleton. myapp-persistence module doesn't really care how to get that config. Right now it's taken from sys variables.
myapp-rest is play-app and it wants to read conf from application.conf and inject it into other components using Guice.
myapp-backend does more or less the same.
Right now I'm locked myself with
#Provides
def provideDatabaseConnectionConfiguration: DatabaseConnectionConfiguration = {
DatabaseConnectionConfiguration.fromSysEnv
}
And I don't understand how to make it flexible and configurable for myapp-rest and myapp-backend.
UPD
According to answer, I did it this way:
Defined trait
trait DbConfProvider {
def dbConf: DbConf
}
Singleton factory now depends on provider:
class HibernateSessionFactoryProvider #Inject()(dbConfProvider: DbConfProvider) extends Provider[SessionFactory] {
}
myapp-persistence module exposes public guice module with all piblic persistence module DAO.
myapp-persistence has module used only for testing purposes. myapp-persistence Injector load module described below:
class MyAppPersistenceDbConfModule extends AbstractModule {
override def configure(): Unit = {
bind(classOf[DbConfProvider]).to(classOf[DbConfSysEnvProvider])
}
}
DbConfSysEnvProvider reads DB connection settings from sys env. Non production use case.
Play app has it's own conf mechanism. I've added my custom module to app conf:
# play-specific config
play.modules.enabled += "common.components.MyAppPersistenceDbConfModule"
# public components from myapp-persistence module.
play.modules.enabled += "com.myapp.persistence.connection.PersistenceModule"
And my configuration service:
#Singleton
class ConfigurationService #Inject()(configuration: Configuration) extends DbConfProvider {
...}
I am not an expert on Play-specific setup, but generally this kind of design problem is solved in one of the following ways:
No default. Remove the binding of DatabaseConnectionConfiguration from the upstream module (myapp-persistence), and define it in each downstream module (myapp-backend, myapp-rest) as appropriate.
Default with override. Keep the default binding of DatabaseConnectionConfiguration like you did, implementing the most common configuration strategy there. Override it in downstream modules using Guice Modules.override(..) API when needed.
Implement a unified configuration mechanism across the modules, that does not depend on particular frameworks used. (E.g. Bootique, which is built on Guice ... Haven't used it with Play though).
I personally prefer the approach #3, but in the absence of something like Bootique, #2 is a good substitute.

Why getting a 202 in two equal setup structuremap code paths

In the C# language, using StructureMap 2.5.4, targeting .NET Framework 3.5 libraries.
I've taken the step to support multiple Profiles in a structure map DI setup, using ServiceLocator model with Bootstrapper activation. First setup was loading default registry, using the scanner.
Now I like to determine runtime what Registry configuration I like to use. Scanning and loading multiple assemblies with registries.
Seems it's not working for the actual implementation (Getting the 202, default instance not found), but a stripped test version does work. The following setup.
Two assemblies containing Registries and implementations
Scanning them in running AppDomain, providing the shared Interface, and requesting Creation Of Instance, using the interfaces in constructor (which get dealt with thanx to the profile on Invokation)
Working code sample below (same structure for other setup, but with more complex stuff, that get's a 202):
What type of couses are possible for a 202, specifically naming the System.Uri type, not being handles by a default type?? (uri makes no sense)
// let structure map create instance of class tester, that provides the registered
// interfaces in the registries to the constructor of tester.
public class Tester<TPOCO>
{
private ITestMe<TPOCO> _tester;
public Tester(ITestMe<TPOCO> some)
{
_tester = some;
}
public string Exec()
{
return _tester.Execute();
}
}
public static class Main {
public void ExecuteDIFunction() {
ObjectFactory.GetInstance<Tester<string>>().Exec();
}
}
public class ImplementedTestMe<TSome> : ITestMe<TSome>
{
public string Execute()
{
return "Special Execution";
}
}
public class RegistryForSpecial : Registry
{
public RegistryForSpecial()
{
CreateProfile("Special",
gc =>
{
gc.For(typeof(ITestMe<>)).UseConcreteType(typeof(ImplementedTestMe<>));
});
}
}
Background articles on Profiles I used.
How to setup named instances using StructureMap profiles?
http://devlicio.us/blogs/derik_whittaker/archive/2009/01/07/setting-up-profiles-in-structuremap-2-5.aspx
http://structuremap.sourceforge.net/RegistryDSL.htm
EDIT:
It seemed the missing interface was actually the one being determined runtime. So here is the next challange (and solved):
I provided a default object whenever StructureMap needs to create the object. Like:
x.ForRequestedType<IConnectionContext>()
.TheDefault.Is.Object(new WebServiceConnection());
This way I got rid of the 202 error, because now a real instance could be used whever structure map needed the type.
Next was the override on runtime. That did not work out at first using the ObjectFactory.Configure method. Instead I used the ObjectFactory.Inject method to overide the default instance. Works like a charm.
ObjectFactory.Inject(typeof(IConnectionContext), context);
Loving the community effort.
Error code 202 means a default instance could not be built for the requested type. Your test code is apparently not equal to your real code that fails. If you are getting an error about Uri, you likely have a dependency that requires a Uri in its constructor. It may not be the class you are asking for - it may be one of that classes dependendencies - or one of the dependencies dependencies... somewhere down the line someone is asking StructureMap to resolve a Uri, which it cannot do, without some help from you.

Resources