I am working on AspectJ AOP implementation on spring MVC application. I have written Aspect java class where I am trying to intercept join points for all the methods of one of the packages say com.xyz.services. But AOP always failed to interecept the methods of that package. Aspect is defined as follows -
#Pointcut("execution(* com.xyz.services..*.*(..))")
public void logBefore() {
}
#Before("logBefore()")
public void logHere(JoinPoint joinPoint) {
System.out.println("In logHere ....");
logger.info("logBefore is running ....");
logger.info("hijacked ::::" + joinPoint.getSignature().getName());
logger.info("joinPoint.getSignature().getDeclaringTypeName() ::::"
+ joinPoint.getSignature().getDeclaringTypeName());
logger.info("joinPoint.getSignature().getModifiers() ::::"
+ joinPoint.getSignature().getModifiers());
logger.info("******************************************************");
}
I have enabled AOP in application-context.xml as follows -
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name='loggingAspect' />
</aop:aspectj-autoproxy>
When ever I am calling webservice the methods in the com.xyz.services get called but the Aspect method never get called.
I tried with different pointcuts but the aspect code never did not execute.
#Pointcut("execution(public * com.xyz.services.ManagerServiceImpl.*(..))")
public void logBefore() {
System.out.println("In Logbefore");}
#Pointcut("execution(public * com.xyz.services.*.*(..))")
public void logBefore() {
System.out.println("In Logbefore");
}
I have added cglib dependency on pom.xml to enable cglib based proxies.
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
Can anyone help me out why this aspects are not working as expected?
have you configured correctly the class with the annotation ?
#EnableAspectJAutoProxy(proxyTargetClass = true)
Related
We had an integration tests such as the one that follows that used to work:
#ActiveProfiles("local")
#WithMockUser("j_unit_user_http_test")
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = { Application.class },
webEnvironment = RANDOM_PORT
)
#Transactional
#Rollback
#AutoConfigureMockMvc()
public abstract class HttpTest {
static {
//reads and sets a dependency related props
PropertiesReader propertiesReader = new PropertiesReader();
propertiesReader.readDependencyProperties().forEach(System::setProperty);
}
#Autowired
private MockMvc mockMvc;
#PersistenceContext
private EntityManager em;
#Test
public void createDashboard() {
// POST is a utility method that wraps "mockMvc.perform(post(url))", I've omitted it here for brevity.
var postResult = POST("/api/dashboards", Map.of("name", "wonderland"));
var newDashboard = extractJson(postResult);
assertTrue(newDashboard.get("id").isInt());
}
}
Among the changes we made the significant ones that seem to be causing the errors are:
Upgrading spring-boot from '2.3.0' to '2.5.6'
Setting the environment properties needed by some of our dependencies in the static void main class of our app:
public class Application {
public static void main(String[] args) {
// reads and sets dependency related props
PropertiesReader propertiesReader = new PropertiesReader();
propertiesReader.readDependencyProperties().forEach(System::setProperty);
}
}
The error we get is:
java.lang.StackOverflowError
at java.base/java.lang.Throwable.getOurStackTrace(Throwable.java:828)
at java.base/java.lang.Throwable.getStackTrace(Throwable.java:820)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:55)
at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:60) // several frames of this follow
My guess is that the mockMvc is not getting configured correctly, because if I use it without #SpringBootTest and #AutoConfigureMvc, the tests work. Any idea what could be wrong?
The above issue has been fixed.
In the commit when the tests started failing, among other changes, the spring-boot version was changed from 2.3.x to 2.5.x
Turns out in version 2.4 spring-boot removed JUnit 5's vintage engine.
As pointed out in their release notes, ideally, i should migrate the tests to junit 5, but, in the meantime, adding the following in build.gradle helps:
testImplementation("org.junit.vintage:junit-vintage-engine") {
exclude group: "org.hamcrest", module: "hamcrest-core"
}
Equivalent changes in pom.xml would be:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
I came across an interesting article: AOP Aspects as mocks in JUnit
Since I have requirement to mock multiple final and private static variables, I am planning to use AOP in place of reflection or PowerMockito as they are causing issues with SpringJUnit4ClassRunner.
Is there any way I can use #Aspect for test classes without using the annotation #EnableAspectJAutoProxy? (I want to use an aspect targeting class X only in one test case.)
This is a sample of what I want to do.
The question is answered(adding for discussion on what could be done)
//External class
public final class ABC(){
public void method1() throws Exception {}
}
#Service
public void DestClass() {
private static final ABC abc = new ABC();
public Object m() {
// code (...)
try {
abc.method1();
}
catch(Exception e) {
// do something (...)
return null;
}
// more code (...)
}
}
Spring framework allows to programmatically create proxies that advise target objects , without configuring through #EnableAspectJAutoProxy or <aop:aspectj-autoproxy>
Details can be found in the documentation section : Programmatic Creation of #AspectJ Proxies and the implementation is pretty simple.
Example code from the documentation.
// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// add an aspect, the class must be an #AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);
// you can also add existing aspect instances, the type of the object supplied must be an #AspectJ aspect
factory.addAspect(usageTracker);
// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();
Please note that with Spring AOP , only method executions can be adviced. Excerpt from the documentation
Spring AOP currently supports only method execution join points
(advising the execution of methods on Spring beans). Field
interception is not implemented, although support for field
interception could be added without breaking the core Spring AOP APIs.
If you need to advise field access and update join points, consider a
language such as AspectJ.
The document shared with the question is about aspectj and without providing the sample code to be adviced it is hard to conclude if the requriement can acheived through Spring AOP. The document mentions this as well.
One example of the integration of AspectJ is the Spring framework,
which now can use the AspectJ pointcut language in its own AOP
implementation. Spring’s implementation is not specifically targeted
as a test solution.
Hope this helps.
--- Update : A test case without using AOP ---
Consider the external Class
public class ABCImpl implements ABC{
#Override
public void method1(String example) {
System.out.println("ABC method 1 called :"+example);
}
}
And the DestClass
#Service
public class DestClass {
private static final ABC service = new ABCImpl();
protected ABC abc() throws Exception{
System.out.println("DestClass.abc() called");
return service;
}
public Object m() {
Object obj = new Object();
try {
abc().method1("test");
} catch (Exception e) {
System.out.println("Exception : "+ e.getMessage());
return null;
}
return obj;
}
}
Following test class autowires the DestClass bean with overridden logic to throw exception . This code can be modified to adapt to your requirement.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { DestClassSpringTest.TestConfiguration.class })
public class DestClassSpringTest {
#Configuration
static class TestConfiguration {
#Bean
public DestClass destClass() {
return new DestClass() {
protected ABC abc() throws Exception {
// super.abc(); // not required . added to demo the parent method call
throw new Exception("Custom exception thrown");
}
};
}
}
#Autowired
DestClass cut;
#Test
public void test() {
Object obj = cut.m();
assertNull(obj);
}
}
Following will be the output log
DestClass.abc() called // this will not happen if the parent method call is commented in DestClassSpringTest.TestConfiguration
Exception : Custom exception thrown
The article you are referring to is using full AspectJ, not Spring AOP. Thus, you do not need any #EnableAspectJAutoProxy for that, just
either the AspectJ load-time weaver on the command line when running your test via -javaagent:/path/to/aspectjweaver.jar
or the AspectJ compiler activated when compiling your tests (easily done via AspectJ Maven plugin if you use Maven)
Both approaches are completely independent of Spring, will work in any project and even when using Spring also work when targeting execution of third party code because no dynamic proxies are needed unlike in Spring AOP. So there is no need to make the target code into a Spring bean or to create a wrapper method in your application class for it. When using compile-time weaving you can even avoid weaving into the third party library by using call() instead of execution() pointcut. Spring AOP only knows execution(), AspectJ is more powerful.
By the way: Unfortunately both your question and your comment about the solution you found are somewhat fuzzy and I do not fully understand your requirement. E.g. you talked about mocking final and private static variables, which would also be possible in other ways with AspectJ by using set() and/or get() pointcuts. But actually it seems you do not need to mock the field contents, just stub the results of method calls upon the objects assigned to those fields.
I have our aspect, annotation & MVC controller written as follows:
Aspect
#Aspect
public class AuditAspect {
#Around(value = "#annotation(com.test.Audit)")
public Object audit(ProceedingJoinPoint pjp) {
System.out.println("Inside the Audit aspect ...");
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable t) {
}
return result;
}
}
The annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Audit
{
AuditType auditType();
}
The controller:
#RestController
#RequestMapping("/patients")
public class PatientController {
#Audit(auditType = AuditType.PATIENT_LIST)
#RequestMapping(value="", method=RequestMethod.GET)
public APIResponse getPatients(HttpServletRequest request, HttpServletResponse response, #RequestParam(required = false, value="audit") String sAudit) {
System.out.println("Inside getPatients ...");
return null;
}
}
However, the aspect's audit method is not getting called whenever I make rest requests.
Looked around for some help. Found few posts where there were mentions of AspectJ not working with Spring MVC controllers. However, I tried this same example with a simple spring MVC application, and the aspect was getting called properly, even if controller methods were annotated. Not sure what is going wrong here. Any pointers/suggestions here would be very helpful.
The sample application I tried didn't have use of spring transaction manager, or integration with hibernate etc... Would that make any difference?
Also, given below is the context file entries:
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.test">
<context:include-filter type="aspectj" expression="com.test.AuditAspect" />
</context:component-scan>
<context:annotation-config />
In order to make Spring AOP work, both your aspect and the target object must be a Spring #Component.
I wanted to implement Jawr for minification and obfuscation of CSS and JS resources. I am using a Spring MVC project where the configurations are done using Java code using annotations and XML is not used. Now able to find a documentation for the same. Can some one suggest me the configuration or a reference link.
jawr dependencies to your pom:
<dependency>
<groupId>net.jawr</groupId>
<artifactId>jawr-core</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>net.jawr.extensions</groupId>
<artifactId>jawr-spring-extension</artifactId>
<version>3.6</version>
</dependency>
Build a controller like this:
#Controller
public class JsController extends JawrSpringController
{
public JsController ()
{
setType("js");
setConfigLocation("/jawr.properties");
}
#RequestMapping("/js/**")
public void javascript ( HttpServletRequest request, HttpServletResponse response ) throws Exception
{
handleRequest(request, response);
}
}
currently i am trying to remove a ConversationScoped Stateful Session Bean (SFSB). The ConversationScope is managed by the CDI Container and the lifecycle of the SFSB is managed by the EJB Container. Is this correct?
In my Controller i'm trying to end the conversation by calling a method of the SFSB and to call the #Remove annotated method to destroy the SFSB.
The conversation can be end without any problems but i am not able to destroy the SFSB.
A Code example from Weld Reference Guide (WELD Conversation Scope):
#ConversationScoped #Stateful
public class OrderBuilder {
private Order order;
private #Inject Conversation conversation;
private #PersistenceContext(type = EXTENDED) EntityManager em;
#Produces public Order getOrder() {
return order;
}
public Order createOrder() {
order = new Order();
conversation.begin();
return order;
}
public void addLineItem(Product product, int quantity) {
order.add(new LineItem(product, quantity));
}
public void saveOrder(Order order) {
em.persist(order);
conversation.end();
}
#Remove
public void destroy() {}
}
The controller:
#Named
#SessionScoped
public class TestController implements Serializable{
#Inject
private OrderBuilder orderBuilder;
...
public String checkout(Order order){
orderBuilder.saveOrder(order);
orderBuilder.destroy();
return "success";
}
}
After i have called testController.checkout(order), i'am getting this exception:
javax.servlet.ServletException:
java.lang.reflect.InvocationTargetException
javax.faces.webapp.FacesServlet.service(FacesServlet.java:321)
org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:67)
root cause
javax.faces.el.EvaluationException:
java.lang.reflect.InvocationTargetException
javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:98)
com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:98)
javax.faces.component.UICommand.broadcast(UICommand.java:311)
javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:781)
javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1246)
com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:77)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:308)
org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:67)
Any ideas?
THX
You should end the CDI conversation and CDI will call the #Remove method.
Have a look in Weld documentation :
"
Stateful session beans may define a remove method, annotated #Remove, that is used by the application to indicate that an instance should be destroyed. However, for a contextual instance of the bean—an instance under the control of CDI—this method may only be called by the application if the bean has scope #Dependent. For beans with other scopes, the application must let the container destroy the bean.
"
JSF 1.2 or 2.0 does not support expression like methods with parameter
obj.method(parameter)
JSF supoorts only method without parameter like
obj.method()
Seam 2,3 build in supports this kind of expreesions but if you are using only weld (CDI support of seam, core of seam) without other seam jars, you can not have this ability.
But it is possible to give this kind of ability to JSF.
Adding this to jar project you can use methods with parameters. If you are using maven u can use code below or. Download jars manually in lib folder.
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
</dependency>
Additionally, I tested it with tomcat worked fine, but in jetty some conflicts happen with the other jars. May be it is about my project.
Add this to web xml
<context-param>
<param-name>com.sun.faces.expressionFactory</param-name>
<param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>