X-Ray trace doesn't shows inner method call - java

I'm new to aws x-ray and trying to use x-ray with AOP based approach in a springboot application. I was able to get the traces in the aws console, but traces doesn't show inner method call method2() details. Am I missing anything here.
Controller class
import com.amazonaws.xray.spring.aop.XRayEnabled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/xray")
#XRayEnabled
public class XrayController {
#GetMapping(value = "/method1")
public String method1() {
return method2();
}
public String method2() {
return "Hello";
}
}
Aspect Class
import com.amazonaws.xray.entities.Subsegment;
import com.amazonaws.xray.spring.aop.BaseAbstractXRayInterceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.Map;
#Aspect
#Component
public class XRayInspector extends BaseAbstractXRayInterceptor {
#Override
protected Map<String, Map<String, Object>> generateMetadata(ProceedingJoinPoint proceedingJoinPoint, Subsegment subsegment) {
return super.generateMetadata(proceedingJoinPoint, subsegment);
}
#Override
#Pointcut("#within(com.amazonaws.xray.spring.aop.XRayEnabled) && (bean(*Controller) || bean(*Service) || bean(*Client) || bean(*Mapper))")
public void xrayEnabledClasses() {}
}
When I hit http://localhost:8080/xray/method1 endpoint,
AWS Xray Console doesn't show method2() details

As I later understood with the use of M. Deinum's comment AOP prevents you to wrap a function if it is in the caller is in the same class of the callee.
Read more on AOP https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-understanding-aop-proxies
You can use a work around using below self injection
public class Test {
#Autowire
private Test test;
public void method1(){
.........
test.method2();
...........
}
public void method2(){
...............
...............
}
}
notice here we call the method 2 by test.method2(); instead of this.method2()
I referred below answer also for this solution Spring AOP not working for method call inside another method

Related

Spring Autowiring "forgot" about dependency

I wanted to play around with the different types of bean scopes. So I wrote a test environment which should generate a random number so I could see if a bean had changed. My test setup does not work and I can not explain what I found out.
I'm using Spring Boot 2.13 with the Spring Framework 5.15.
Following setup:
Main class:
package domain.webcreator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class WebcreatorApplication {
public static void main(String[] args) {
SpringApplication.run(WebcreatorApplication.class, args);
}
}
Beans class:
package domain.webcreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Random;
#Configuration
public class Beans {
#Bean
public Random randomGenerator() {
return new Random();
}
}
Scoper class:
package domain.webcreator;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.util.Random;
#Service
#Scope("singleton")
public class Scoper {
private Random rand;
public Scoper(Random rand) {
this.rand = rand;
}
public int getNumber(int max) {
return rand.nextInt(max);
}
}
Index Controller
package domain.webcreator.controller;
import domain.webcreator.Scoper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class IndexController {
#GetMapping("/")
#ResponseBody
#Autowired
public String indexAction(Scoper scoper) {
return String.valueOf(scoper.getNumber(50));
}
}
My problem is, that I get an NPE while calling scoper.getNumber(50).
This is strange because when debugging, a Random bean is generated and passed to the scoper constructor.
Later on, in the controller, the rand property is null.
What am I doing wrong?
You're trying to apply #Autowired to a random method, which isn't how Spring works. Controller method parameters are for information specific to that HTTP request, not general dependencies, and so Spring is trying to create a new Scoper that is associated with the request--but it doesn't have any incoming values in the request to fill in. (I'm actually surprised you're not getting an error about no default constructor.)
Instead, pass your Scoper in a constructor.
#RestController
public class IndexController {
private final Scoper scoper;
public IndexController(Scoper scoper) {
this.scoper = scoper;
}
#GetMapping("/")
public String indexAction(Scoper scoper) {
return String.valueOf(scoper.getNumber(50));
}
}
A couple of notes:
Singleton scope is the default, and there's no need to specify it.
#RestController is preferable to repeating #ResponseBody unless you have a mixed controller class.

Registering a custom ValueParamProvider in Jersey 2.27

I realize that these are internal APIs, but if they're available internally why not make them usable by the less privileged masses, and they're also extremely useful. Even though these APIs were internal in Jersey 2.25 they could be used, and I'd like to upgrade my Jersey version without breaking my custom Jersey extensions.
It's certainly possible to extend ValueParamProvider in Jersey 2.27, but I no longer see a way to register that Provider along with it's triggering annotation. Looking at how Jersey does this for its own implementations, it now uses a BoostrapConfigurator, which seems to be internalized to such an extent that external implementations can't use the same methodology.
Maybe I'm wrong about that, and if someone has a clear description of how, that would be great. Otherwise, does anyone know of a method for doing the same thing?
This used to work...
ResourceConfig resourcceConfig = ...
resourceConfig.register(new AbstractBinder() {
#Override
protected void configure (){
bind(MyParamValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(MyParamInjectionResolver.class).to(new TypeLiteral<InjectionResolver<EntityParam>>() {
}).in(Singleton.class);
}
}
});
With appropriate implementations of AbstractValueFactoryProvider and ParamInjectionResolver.
Now it looks like you need to implement ValueParamProvider, which is easy enough, but I'm not sure how to register that properly with the Jersey framework anymore. Any help appreciated.
You don't need to use any BootstrapConfigurator. All you need to is add the services to the injector and they will be added later to the list of value providers.
To configure it, you can still use the AbstractBinder, but instead of the HK2 one, use the Jersey one. The ValueParamProvider can still be bound the same way, but for the InjectionResolver, you should make sure to implement not the HK2 resolver, but the Jersey one. Then instead of binding to TypeLiteral, bind to GenericType.
I just want to add that a misconception that people have when trying to implement parameter injection is that we also need an InjectResolver to use a custom annotation for the method parameter. This is not the case. The method parameter annotation is just a marker annotation that we should check inside ValueParamProvider#getValueProvider() method. An InjectResolver is only needed for non-method-parameter injections, for instance field and constructor injection. If you don't need that, then you don't need the InjectionResolver.
Below is a complete example using Jersey Test Framework. I didn't use an InjectionResolver, just to show that it's not needed.
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.core.Response;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
public class ParamInjectTest extends JerseyTest {
#Target({ElementType.PARAMETER, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Auth {
}
private static class User {
private String username;
public User(String username) {
this.username = username;
}
public String getUsername() {
return this.username;
}
}
public static class AuthValueParamProvider implements ValueParamProvider {
#Override
public Function<ContainerRequest, ?> getValueProvider(Parameter parameter) {
if (parameter.getRawType().equals(User.class)
&& parameter.isAnnotationPresent(Auth.class)) {
return new UserParamProvider();
}
return null;
}
private class UserParamProvider implements Function<ContainerRequest, User> {
#Override
public User apply(ContainerRequest containerRequest) {
return new User("Peeskillet");
}
}
#Override
public PriorityType getPriority() {
return Priority.HIGH;
}
}
public static class AuthFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder() {
#Override
protected void configure() {
bind(AuthValueParamProvider.class)
.to(ValueParamProvider.class)
.in(Singleton.class);
}
});
return true;
}
}
#Path("test")
#Consumes("text/plain")
public static class TestResource {
#POST
#Produces("text/plain")
public Response post(String text, #Auth User user) {
return Response.ok(user.getUsername() + ":" + text).build();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.register(AuthFeature.class);
}
#Test
public void testIt() {
final Response response = target("test")
.request()
.post(Entity.text("Test"));
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(String.class)).isEqualTo("Peeskillet:Test");
}
}
Another thing I'll mention is that in previous versions where you extended AbstractValueFactoryProvider and implemented a ParamInjectionResolver, most people did this to follow how Jersey implemented parameter injection while still allowing for other injection points (field and constructor). If you still want to use this pattern, you can.
Below is the AuthFeature from the above test refactored
public static class AuthFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
InjectionManager im = InjectionManagerProvider.getInjectionManager(context);
AuthValueParamProvider authProvider = new AuthValueParamProvider();
im.register(Bindings.service(authProvider).to(ValueParamProvider.class));
Provider<ContainerRequest> request = () -> {
RequestProcessingContextReference reference = im.getInstance(RequestProcessingContextReference.class);
return reference.get().request();
};
im.register(Bindings.injectionResolver(new ParamInjectionResolver<>(authProvider, Auth.class, request)));
return true;
}
}
I figured this stuff out just digging through the source. All this configuration I saw in the ValueParamProviderConfigurator. You don't need to implement your own ParamInjectionResolver. Jersey has a concrete class already that we can just use, as done in the feature above.
If you change the TestResource to inject by field, it should work now
#Path("test")
#Consumes("text/plain")
public static class TestResource {
#Auth User user;
#POST
#Produces("text/plain")
public Response post(String text) {
return Response.ok(user.getUsername() + ":" + text).build();
}
}

Spring AOP - aspect not working without xml configuration

My intention is run the aspect before get message method in service. I don't want to use xml configuration, so I add (hopefully) necessary annotation. But, when I run my application, aspect doesen't work, and nothing happen. Can You explain me why?
Service
public interface Service {
void getMessage();
}
Service implementation
import org.springframework.stereotype.Component;
#Service
public class ServiceImpl implements Service {
public void getMessage() {
System.out.println("Hello world");
}
}
Aspect
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class LoggingAspect {
#Before("execution(* com.example.aop.Service.getMessage())")
public void logBefore(JoinPoint joinPoint) {
System.out.println("AOP is working!!!");
}
}
Run
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass=true)
#ComponentScan("com.example")
public class AopApplication {
public static void main(String[] args) {
final ConfigurableApplicationContext run = SpringApplication.run(AopApplication.class, args);
final Service bean = run.getBean(ServiceImpl.class);
bean.getMessage();
}
}
Output
Only Hello world
Probably, you have to add #Component annotation to LoggingAspect class.
I believe the pointcut expression syntax should be like this:
#Before("execution(void com.aop.service.Service+.getMessage(..))")
The + is used to apply the pointcut to subtypes (you can replace void with * too.

Spring AOP can't apply advice to interface

I am learning spring the from book the "Spring in Action fourth edition" by Craig Walls. I am trying to apply advice to the method declared by the interface and I am getting Exception. When I apply the same advice to the class which doesn't implement anything, everything works fine.
Spring version - 4.3.2
Help would be appreciated.
Exception:
Exception in thread "main"org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.fifczan.bean.UserService] is defined
Code:
Interface:
package com.fifczan.bean;
public interface Service {
void doTask();
}
Implementation:
package com.fifczan.bean;
import org.springframework.stereotype.Component;
#Component
public class UserService implements Service {
public void doTask() {
System.out.println("doing task");
}
}
Aspect:
package com.fifczan;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class UserAspect {
//If i change Service(interface) to UserService(implementation)
//in pointcut I am getting the same exception
#Before("execution(* com.fifczan.bean.Service.doTask(..))")
public void userAdvice(){
System.out.println("doing sth before method doTask");
}
}
Configuration:
package com.fifczan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
public class AspectJAutoProxyConfig {
}
main :
package com.fifczan;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.fifczan.bean.UserService;
public class AspectJAutoProxyTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AspectJAutoProxyConfig.class);
UserService userService= ctx.getBean(UserService.class);
userService.doTask();
}
}
You're asking for a bean of UserService, which is the concrete class, not the interface. Retrieve or inject a bean of type Service.

Interface object on a spring controller

I saw a tutorial of spring but I have some doubts about it
If I had a interface like this
package com.journaldev.spring.service;
import java.util.List;
import com.journaldev.spring.model.Person;
public interface PersonService {
public void addPerson(Person p);
public void updatePerson(Person p);
public List<Person> listPersons();
public Person getPersonById(int id);
public void removePerson(int id);
}
and a class which implements the interface
package com.journaldev.spring.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.journaldev.spring.dao.PersonDAO;
import com.journaldev.spring.model.Person;
#Service
public class PersonServiceImpl implements PersonService {
private PersonDAO personDAO;
public void setPersonDAO(PersonDAO personDAO) {
this.personDAO = personDAO;
}
.
.
.
}
and the controller which use the service
package com.journaldev.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.journaldev.spring.model.Person;
import com.journaldev.spring.service.PersonService;
#Controller
public class PersonController {
private PersonService personService;
#Autowired(required=true)
#Qualifier(value="personService")
public void setPersonService(PersonService ps){
this.personService = ps;
}
.
.
.
}
Why the controller has a PersonService object (that in an interface) instead of an PersonServiceIml(class which implements the interface) object????
The idea is that designing to interfaces is good practice : What does "program to interfaces, not implementations" mean?
It makes creating a new implementation easy and refactoring is simpler. An interface also ensures mocking is straightforward.
In practice you can get rid of the interface, mockito/powermock etc handle simple classes fine, and in lots of cases you won't need a new implementation or any refactoring.
Not sure whether the question is,
Why to use interface or
How the methods are called of the implemented class.
Assuming that, you want to know how the implemented methods are called, please check your dependency injection file. You will see that you have injected your implemented class.
Say for example, it might look something like this,
<bean id="personService" class="com.somethimg.service.impl. PersonServiceImpl">
</bean>

Categories

Resources