Spring - Is it good to register request scoped bean at every request though code? - spring

In Spring Web application, I have to use specific value from request object in another spring classes within application. Value is request specific value.
In the following example, is it good way to register value coming from Request Body every time and use #Autowired with #RequestScope annotation to use value in another spring(e.g. #Service) classes? Is it good to register RequestScopedType bean value for each request through BeanFactory?
#RestController
#RequestMapping("/")
public class VehicleServiceController {
#Autowired
private BeanFactory beanFactory;
#Autowired
private ServiceClass serviceClass;
#PostMapping(path = "/postDetails", consumes = MediaType.APPLICATION_JSON_VALUE)
public OutputPayload postDetails(
#RequestBody InputPayload inboundPayload) throws Exception {
beanFactory.getBean(RequestScopedType.class).setValue(inboundPayload.getType());
return serviceClass.methodToCall();
}
}
Will there be any impact on performance as load is very huge? Is there any another way to inject/get RequestBody object value(inboundPayload.getType())?

You don't have to do beanFactory.getBean(RequestScopedType.class). You can just simply autowire it #Autowired RequestScopedType requestScopedType.
Just don't forget to change the scope of the bean as Request.
#Component
#Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedType {
But this begs another question, why over complicate things, why can't you pass inboundPayload.getType() to the serviceClass.methodToCall(); ?
What is stopping you from using it this way return serviceClass.methodToCall(inboundPayload.getType());

Related

Mockito #Spy calls real method while mocking behaviour is defined

I have a common spring boot application consisting of a controller and a service layer. A database is not involved as I use another rest api to store the data.
Now I want to the my controller and therefor I want to mock my sevice partially. Partially because I have one method in it that takes a dto and converts it to my business model. I know this can also be solved with a constructor of the bussiness model but anyway I came to the following problem:
CODE
Controller
#RestController
public class RegistrationController {
#Autowired
private UserRegistrationService userRegistrationService;
#PostMapping(value = "/user", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UserId> createUser(#RequestBody #Valid UserDto userDto) {
KeycloakUserRepresentation keycloakUserRepresentation = userRegistrationService.convertUserDtoToKeycloakUserRepresentation(userDto);
UserId userId = userRegistrationService.createNewUser(keycloakUserRepresentation);
return new ResponseEntity<>(userId,HttpStatus.CREATED);
}
TEST
#SpringBootTest
#AutoConfigureMockMvc
#ExtendWith({RestDocumentationExtension.class})
#AutoConfigureRestDocs
class RegistrationControllerRegistrationTest {
private static final UserDto testUsertDto = new UserDto();
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Spy
private UserRegistrationServiceImpl userRegistrationService;
In my test method I define:
doReturn(testUserId).when(userRegistrationService).createNewUser(any(KeycloakUserRepresentation.class));
PROBLEM:
I expect that while not define a doSomething in my test, the conversion of the userDto to a keycloak representation is done by the original method. This seems to work as when I debug in my Controller the keycloakUserRepresentation has the correct values. The problem is that in the next step the
createNewUser
method is not stubbed/mocked. The original method is executed and so my test fails.
What I want is that when I provide a doSomething method in my testcase, I want the original method to be mocked.
This seems to work as when I debug in my Controller the
keycloakUserRepresentation has the correct values.
It worked because the bean wasn't spied on at all. Although #Spy may help in creating a mocked object, it does not result in a mocked Spring bean in your test application context. Use #SpyBean to have Mockito spy on a bean in the Spring application context.

Authentication using request scoped context with Spring

I am trying to create custom "userContext" as a SpringBean with request scope but I am unable to do so. Basically I have a Jersey REST api and I want to do authentication and authorization using my custom filters in which I autowire my "userContext" bean. The process looks like this:
REST API called (I expect Spring to create new instance of userContext bean)
AuthenticationFilter autowires new instance of userBean and populates it
AuthorizationFilter autowires the same instance which is populated now and authorize the user
When i first call the REST api (after server restart), it works as expected, but any other call fails, because AutorizationFilter gets an empty instance of userBean. I expect some fundamental misunderstanding of scopes on my part.
Btw: I'd like to avoid using ThreadLocal directly since Request scope should take care of it
I would like to know, why authorizationFilter doesn't see the populated version of the userBean and why the first call works. Thanks in advance for any help.
Just some parts of the code:
#Secured({Role.ADMIN}) //custom annotation
#GET
#Path("{id}")
public Response getUserById(#PathParam("id") Long id) throws IOException, MainException {
#Secured //custom annotation
#Provider
#Priority(Priorities.AUTHENTICATION)
#Scope(value="request", proxyMode= ScopedProxyMode.TARGET_CLASS)
public class AuthenticationFilter implements ContainerRequestFilter
#Autowired
private User userContext;
#Secured //custom annotation
#Provider
#Priority(Priorities.AUTHORIZATION)
#Scope(value="request", proxyMode= ScopedProxyMode.TARGET_CLASS)
public class AuthorizationFilter implements ContainerRequestFilter {
#Autowired
private User userContext;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class User extends ModelBase implements Serializable {
Since Providers are not request scoped, I had to inject whole ApplicationContext so I could directly modify the right instance of userContext bean (which is request scoped). Basically I did something like this in both filters:
#Autowired
private ApplicationContext applicationContext;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
User userBean=applicationContext.getBean(User.class);
....
userBean.setSomething("aaa");
Then I could just autowire such bean in my REST Resources, because they are request scoped by defaul:
#Component
#Api(value="/users", description = "Endpoint for Users listing")
#Consumes({MediaType.APPLICATION_JSON, Constants.API_VERSIONS.V1_HEADER_XML, Constants.API_VERSIONS.V1_HEADER_JSON})
#Produces({MediaType.APPLICATION_JSON, Constants.API_VERSIONS.V1_HEADER_XML, Constants.API_VERSIONS.V1_HEADER_JSON})
#Path("/users")
public class UserResource {
private static final Logger logger = LoggerFactory.getLogger(UserResource.class);
#Autowired
private User authenticatedUser;
This solution should be viable and resistent against thread race conditions etc.

not able to replace spring bean with mock in camel route

using #Profile I am able to mock the spring bean, however in the camel route which mock bean method is not invoked. I am using SpringJUnit4ClassRunner.class and using #ActiveProfile
Below is the route in which I want to replace, cancelSubscriptionTransformer, myBeanClient, extendedClient beans with my mock beans in unit testing.
from("{{cancelSubscriptionFromRMQUri}}").routeId("cancelSubscriptionRoute")
.unmarshal().json(JsonLibrary.Jackson, Subscription.class)
.bean("cancelSubscriptionTransformer", "toKbCancelSubscription")
.choice()
.when().simple("${body.serviceType} == 'subscriptions'")
.bean("myBeanClient", "cancelSubscription(${body.subscriptionId}, ${body.createdBy}, ${body.reason}, ${body.comment})")
.bean("extendedClient", "retrieveSubscription(${body.subscriptionId}, ${body.externalKey})")
.marshal(json)
.to("{{cancelSubscriptionTORMQUri}}")
.when().simple("${body.serviceType} == 'usage'")
.bean("myBeanClient", "cancelSubscription(${body.subscriptionId}, ${body.dateTime},null, null, -1, ${body.createdBy}, ${body.reason}," +
" ${body.comment})")
.endChoice();
Below is how I define my ExtendedClientMock, I use the same approach for the rest of the mock beans
#Profile("test")
#Primary
#Repository
public class ExtendedClientMock extends ExtendedClient {
public Subscription retrieveSubscription(UUID subscriptionid, String sdpSubscriptionId) throws MyClientException {
Subscription subs=new Subscription();
subs.setProductName("test");
return subs;
}
}
Below is the code for unit testing:
#ActiveProfiles({"test", "aop"})
#AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = CancelSubscriptionRouteTest.class)
#EnableAutoConfiguration
#ComponentScan
#ContextConfiguration(classes = { BillingServicesApplication.class })
#UseAdviceWith
public class CancelSubscriptionRouteTest {
#Autowired
protected CamelContext camelContext;
#Autowired
private CancelSubscriptionTransformer cancelSubscriptionTransformer;
#Autowired
private ExtendedClient extendedClient;
#Autowired
private MyBeanClient myBeanClient;
#EndpointInject(uri = "{{cancelSubscriptionTORMQUri}}")
private MockEndpoint cancelSubscriptionTORMQUriEndpoint;
#EndpointInject(uri = "{{cancelSubscriptionFromRMQUri}}")
private ProducerTemplate cancelSubscriptionFromRMQUriEndpoint;
#Inject
private ObjectMapperContextResolver objectMapperContextResolver;
#Test
#DirtiesContext
public void testCancelSubscriptionRoute() throws Exception {
cancelSubscriptionTORMQUriEndpoint.expectedMessageCount(1);
ObjectMapper objectMapper= objectMapperContextResolver.getContext(ObjectMapperContextResolver.class);
String jsonString=objectMapper.writeValueAsString(subscription);
CancelSubscription cancelSubscription=cancelSubscriptionTransformer.toKbCancelSubscription(subscription);
Assert.assertEquals("mock auto created by amel",cancelSubscription.getComment());
cancelSubscriptionFromRMQUriEndpoint.sendBody(" {{cancelSubscriptionFromRMQUri}}",jsonString);
cancelSubscriptionTORMQUriEndpoint.assertIsSatisfied();
}
}
The Assert.assertEquals("mock auto created by amel",cancelSubscription.getComment()); gets statisfied by calling cancelSubscriptionTransformer.toKbCancelSubscription which is invoked on the mock bean. however when message is sent to cancelSubscriptionFromRMQUriEndpoint.sendBody, the route is invoked and the actual beans in the route are not being replaced by mock beans
#MickaƫlB looks like the issue was I was not extending the correct bean and also I had to use #Inject in my route builder spring bean and use bean name instead of string format of bean name
This is very old but I ran into this issue.
The answer is that instead of .Bean(MyBean.class, "myMethod"), you should use .to("bean:myBean?method=myMethod"). The reason is that the first way, Camel will instantiate the bean. The 2nd way, Spring has control of the bean and camel will look it up. Therefore you can use Spring mockBean to change it.
I'm using Camel version 3 now by the way, and beanRef is removed. If you used beanRef, replace it with .to("bean:myBean?method=myMethod).

jersey spring autowiring nullPointerException

I have a session-scoped bean (Ama04Service) in Spring that is set within the web context. I have a task that runs as a Callable, and I need access to this bean from jersey web services. How should I accomplish this? If I simply attempt autowiring the bean the first call of Ama04ServiceImpl (public Response verifNum) the autowiring run successfully, then if i call an another method (clickReponse) in the jersey web service(Ama_04Service) I get NullPointerException
this looks like sprine re-instantiate the autowired bean
please any idea ?
thanks in advance.
The session-scoped bean I am injecting looks like this:
Service("ama04Service")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Transactional(readOnly = false)
public class Ama04ServiceImpl implements Ama04Service, LineListener {
////some code here//////
}
the jersey web service that calls session-scoped bean looks like this
#Path("/ama04")
public class Ama_04Service {
#Autowired
Ama04Service ama04Service;
#Autowired
Ama01Service ama01Service;
#Autowired
Ama03Service ama03Service;
#Autowired
ResourceBundleService resourceBundleService;
#GET
#Path("/quest/{date}/{numDonn}/{language}")
#Produces(MediaType.APPLICATION_JSON)
public Response verifNum(#PathParam("numDonn") final String numDonn,
#PathParam("date") final String date,
#PathParam("language") final String language) {
///////// some code here///////
#GET
#Path("/clickRepons/{numDonn}/{occRep}/{occ}/{date}/{language}")
#Produces("application/json; charset=UTF-8")
public String clickReponse(#PathParam("numDonn") final String numDonn,
#PathParam("occRep") int occRep, #PathParam("occ") int occ,
#PathParam("date") final String date,
#PathParam("language") final String language) {
Ama4 currentAma = new Ama4();
}

Why beans (with request scope) are not initialized in every request in controllers?

My ActionResponse code is :
#Component
#Scope(value = "request",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ActionResponse{
public int a;
//body
}
My Controller:
#Controller
#RequestMapping(value="/ajax/discussion")
public class DiscussionController extends AbstractController {
#Autowired
private ActionResponse actionResponse;
public void setActionResponse(ActionResponse actionResponse) {
this.actionResponse = actionResponse;
}
#RequestMapping("/test")
public #ResponseBody String test(){
String response=this.actionResponse.a+"";
if(this.actionResponse.a==0)
this.actionResponse.a=10;
return response;
}
}
I start the project and then the first time I request /ajax/discussion/test it shows 0
but after that for other requests it shows 10
It has to show 0 in every request because of request scope for ActionResponse
The question is:
Why the bean(ActionResponse) is created once not in every request?!!!
CGLIB works on class level.
CGLIB proxy is still a singleton, so it inherits the fields from the base class. When you change its public properties you change the values of the singleton.
You should encapsulate your data changes in public getters and setters.
Was a little late - Just adding on to Boris Treukhov's answer(have +1'd it):
The reason is that since you have annotated ActionResponse with #Scope(proxyMode=..) Spring ends up creating a CGLIB subclass of this ActionResponse which internally handles the scope appropriately.
Now when you inject ActionResponse into the DiscussionController it is the CGLIB proxy that gets injected, and since you are setting the fields directly with going through the setter, it just modifies the fields of the proxy and not the underlying scoped proxied object. The fix is simply to make state changes via the getters and setters not through fields.

Resources