Spring beans injection into jax-ws services - spring-mvc

So I already learnt that integration of spring and jax-ws is not an easy thing.
I want to inject a spring bean into jax-ws service, but for some reason I get an exception during the deployment:
Exception while loading the app : java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.IllegalArgumentException: javax.servlet.ServletException: com.sun.enterprise.container.common.spi.util.InjectionException: Error creating managed object for class: class org.springframework.web.context.ContextLoaderListener|#]
this is my jax-ws configuration:
<wss:binding url="/ws/users">
<wss:service>
<ws:service bean="#usersWs"/>
</wss:service>
</wss:binding>
<bean id="usersWs" class="love.service.endpoint.implementations.UserServiceImpl" />
And this is my service:
#WebService
public class UserServiceImpl implements UserService{
#EJB
private DBManager dbmanager;
#Override
#WebMethod
public boolean addUser(String name, String password, String email) {
return false;
}
#Override
#WebMethod
public boolean isUsernameAvailable(String username) {
return dbmanager.isLoginAvailable(username);
}
#Override
#WebMethod
public boolean isEmailAvailable(String email) {
return dbmanager.isEmailAvailable(email);
}
}
and finally my bean configuration:
<bean id="dbmanager" class="love.commons.database.DBManager" scope="request">
<aop:scoped-proxy/>
</bean>
I also tried injecting the bean into some controllers and then it works perfectly well.
If I replace #EJB with #Autowired, the application starts, but the service still doesn't work. When I tried sending a message to it, my only response was the following:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
<faultcode>S:Server</faultcode>
<faultstring>Error creating bean with name 'scopedTarget.dbmanager': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.</faultstring>
</S:Fault>
</S:Body>
</S:Envelope>

Related

Initialising OAuth WebClient Bean in Spring MVC

I have a WebApp JSP project deployed on Weblogic 12 as a WAR.
My gradle build includes mvc and webflux:
implementation 'org.springframework.boot:spring-boot-starter-web:2.3.2.RELEASE'
implementation ("org.springframework.boot:spring-boot-starter-security:2.3.2.RELEASE")
implementation ("org.springframework.boot:spring-boot-starter-oauth2-client:2.3.2.RELEASE")
implementation ("org.springframework.boot:spring-boot-starter-webflux:2.3.2.RELEASE")
I am trying to configure OAuth2 to use client_credentials flow from my client JSP application.
I need the #Controller class to use WebClient and propagate the access token to a Resource Server.
My Bean to create the WebClient is seen below.
#Bean
public ReactiveClientRegistrationRepository getRegistration() {
ClientRegistration registration = ClientRegistration
.withRegistrationId("ei-gateway")
.tokenUri("https://xxxxx.xxxxxxx.net/auth/oauth/v2/token")
.clientId("xxx-xxxx-43e9-a407-xxxxx")
.clientSecret("xxxxxx-3d21-4905-b6e5-xxxxxxxxxx")
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.build();
return new InMemoryReactiveClientRegistrationRepository(registration);
}
#Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations, ServerOAuth2AuthorizedClientRepository authorizedClients) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
oauth.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.filter(oauth)
.defaultHeader("accept", "application/json")
.defaultHeader("content-type", "application/json")
.defaultHeader("environment", environment)
.filter(logRequest())
.filter(logResponse())
.build();
}
However I get the following error during compile:
Could not autowire. There is more than one bean of 'ReactiveClientRegistrationRepository' type.
Beans:
clientRegistrationRepository   (ReactiveOAuth2ClientConfigurations.class)
getRegistration   (WebSecurityConfiguration.java)
However when I uncomment out the getRegistration Bean method and configure the oauth client registration via the web.xml, then when deploying the application I get this error:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}:org.springframework.beans.factory.NoSuchBeanDefinitionException:No qualifying bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I see from the ReactiveOAuth2ClientAutoConfiguration source code that the Reactive OAuth2 Auto Configuration is not run when ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition is set.
#Configuration(proxyBeanMethods = false)
#AutoConfigureBefore(ReactiveSecurityAutoConfiguration.class)
#EnableConfigurationProperties(OAuth2ClientProperties.class)
#Conditional(ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition.class)
#ConditionalOnClass({ Flux.class, EnableWebFluxSecurity.class, ClientRegistration.class })
#Import({ ReactiveOAuth2ClientConfigurations.ReactiveClientRegistrationRepositoryConfiguration.class,
ReactiveOAuth2ClientConfigurations.ReactiveOAuth2ClientConfiguration.class })
public class ReactiveOAuth2ClientAutoConfiguration {
}
Can anyone suggest a course of action? Is is possible to manually configure the ReactiveOAuth2ClientConfiguration?
Thanks
Form what I understand ReactiveClientRegistrationRepository is not available since you are not using a reactive stack, and here's how you can set up WebClient to be used in a Servlet environment.
Setup application properties so Spring autowires ClientRegistrationRepository and OAuth2AuthorizedClientRepository for you.
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://xxxxx.xxxxxxx.net/auth/oauth/v2/token
spring.security.oauth2.client.registration.ei-gateway.client-id=xxx-xxxx-43e9-a407-xxxxx
spring.security.oauth2.client.registration.ei-gateway.client-xxxxxx-3d21-4905-b6e5-xxxxxxxxxx
spring.security.oauth2.client.registration.ei-gateway.provider=my-oauth-provider
spring.security.oauth2.client.registration.ei-gateway.scope=read,write
spring.security.oauth2.client.registration.ei-gateway.authorization-grant-type=client_credentials
Setup configuration to indicate that your application needs to act as an oauth2 Client
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.oauth2Client();
}
}
Expose WebClient bean configured to use client credentials
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
#Bean
WebClient webClient(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(
oAuth2AuthorizedClientManager);
// default registrationId - Only if you are not using the webClient to talk to different external APIs
oauth2Client.setDefaultClientRegistrationId("ei-gateway");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
Now you can use WebClient in your code to access external protected resources.
references:
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2client
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2Client-webclient-servlet
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#defaulting-the-authorized-client
This set up worked for me when the application is not configured as a resource server, I had to use a different configuration when the application needs to use WebClient, but also configured to be a resource server.

Why refreshing the ApplicationContext immediately after its creation in WebApplicationInitializer?

In https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html 11.1 there is an example:
public class MyWebApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
...
Then in chapter 11.4 there is another example but without the ac.refresh();
What benefit could refreshing the context immediately after its creation possibly have?
After calling refresh() it would be possible to get all configured contents from the ApplicationContext (beans, listeners, resources, etc.).
For instance, without refreshing the context any call to ac.getBean("myBean") would throw
java.lang.IllegalStateException:
BeanFactory not initialized or already closed - call 'refresh'
before accessing beans via the ApplicationContext
Usually in WebApplicationInitializer there is no need to get beans or other configured items, because its purpose is configuration of servlets and filters for the web application. That's why calling refresh is not necessary here.

Error creating bean with name 'authenticationSuccessHandler' defined in ServletContext resource [/WEB-INF/config/application-security.xml]

I am getting the following error:
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'authenticationSuccessHandler' defined
in ServletContext resource [/WEB-INF/config/application-security.xml]:
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'java.lang.String' available: expected at
least 1 bean which qualifies as autowire candidate. Dependency
annotations: {}
My bean in my IDE looks like so:
LoginSuccessHandler
public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private static final Logger log = LoggerFactory.getLogger(LoginSuccessHandler.class);
private UserDto user = (UserDto) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
private HttpServletRequest request;
public LoginSuccessHandler(String defaultTargetUrl, HttpServletRequest request) {
super(defaultTargetUrl);
this.request = request;
}
When I hover over it it says String defaultTargetUrl ??? and HttpServletRequest ???
In my application-security.xml file I have it defined like so:
<security:form-login login-page="/logon.do"
login-processing-url="/logon.do"
authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-handler-ref="authenticationFailureHandler"
username-parameter="username"
password-parameter="password"/>
<!-- Users limited to one login at a time -->
<security:session-management>
<security:concurrency-control
max-sessions="1" error-if-maximum-exceeded="false" />
</security:session-management>
<security:logout logout-url="/logout" delete-cookies="true"
invalidate-session="true" success-handler-ref="logoutSuccessHandler" />
<!-- enable CSRF -->
<security:csrf disabled="false"/>
</security:http>
This part of the error has me very confused.
No qualifying bean of type 'java.lang.String' available:
This is a compilation error. Any advice with be greatly appreciated.
-----------------------Update one------------------------
I removed the constructors parameters and the error went away. However now I am getting an error saying: .LoginSuccessHandler]: Constructor threw exception; nested exception is java.lang.NullPointerException
The bean looks the same but I read The determineTargetUrl method is the only thing which uses it and request is one of its arguments. Either use a <constructor-arg> in the xml and pass the value for defaultUrl or remove it as an argument and get it elsewhere. But I am not sure where to put it.
Right now the null pointer exception is on this line after removing the constructor:
private UserDto user = (UserDto) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
I am also getting and error saying:
caused By: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name
'org.springframework.security.web.authentication.UsernamePasswordAuthenti cationFilter#0': Cannot resolve reference to bean 'authenticationSuccessHandler' while setting bean property 'authenticationSuccessHandler'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationSuccessHandler' defined in ServletContext resource [/WEB-INF/config/application-security.xml]:
------------------------Update 2---------------------------
I did the following:
public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private static final Logger log = LoggerFactory.getLogger(LoginSuccessHandler.class);
#Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
UserDto user = (UserDto) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Before I was getting a nullPointerException because UserDto user = (UserDto) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); before the method. Now I am getting an error saying:
NoSuchBeanDefinitionException:No bean named
'org.springframework.security.authenticationManager' available: Did
you forget to add a global <authentication-manager> element to your
configuration (with child <authentication-provider> elements)?
Alternatively you can use the authentication-manager-ref attribute on
your <http> and <global-method-security> elements.
The first error is the one on the class you wrote. The constructor ask for a String and for a HTTPSession.
The String can be set as an attribute
The HTTPSession should not be used as a spring bean as Spring declare Singleton and Session depends on user.
The second error means you forgot to provide a authentication manager. You can add one with:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
user-service-ref="userDetailsService" />
</security:authentication-manager>
where userDetailsService is a bean that map a name to a UserDetails.
Spring provides some implementation like InMemoryUserDetailsManager, JdbcUserDetailsManager or LdapUserDetailsService.

Testing with autowired dependency like session in Validator

I have few custom annotations defined on fields of an object like:
public class Person{
#Accountname
String email;
}
Implementation class of #Accountname:
#Autowired ValidationService service;
#Autowired ClientSession clientSession;
#Override
public boolean isValid(final String email, final ConstraintValidatorContext ctx) {
if(!service.isAccountValid(email, clientSession)){
return false;
}
}
I am trying to write junits for these annotations.
#Test
public void validEmailTest()
{
person.setEmail("abc#xyz.com");
Set<ConstraintViolation<Person>> violations = validatorInstance.getValidator().validateProperty(person, "email");
Assert.assertEquals(1, violations.size());
}
But its throwing this error when I execute the test:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.clientSession': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:343)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:34)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.getTarget(CglibAopProxy.java:663)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:614)
at org.hibernate.validator.internal.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:308)
... 45 more
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:90)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:329)
... 54 more
Any idea on how to write junits if a validator class has a dependency on services like session etc.?
This should be tested separately (units).
The real logic that validates is in your ValidationService, so test it there, in AccountnameValidator test only the logic that is in there, injecting your dependencies:
#Mock ValidationService service;
#Mock ClientSession clientSession;
#InjectMocks AccountnameValidator av = new AccountnameValidator()
//initialize mocks
//example test
when(service.isAccountValid(email, clientSession)).thenReturn(true);
boolean result = av.isValid(email, ctx);
assertTrue(result);
And finally if you want you can validate presence of the annotation in Person class on email field using reflection.

Autowired dependency not injected in Aspect in Spring MVC

I am not able to #Autowire the Service Layer Instance in Aspect. In Aspect the reference to the #Autowired bean is NULL and it throws NullPointerException. Any help will be much appreciated. I think, I messed up with configuration.
Following is my servlet-context.xml:
<!-- Activates various annotations to be detected in bean classes -->
<context:annotation-config />
<context:spring-configured />
<!-- Scans the classpath of this application for #Components to deploy as beans -->
<context:component-scan base-package="xx.yy" />
<!-- an #AspectJ aspect will be interpreted as an aspect by Spring AOP and beans in the context will be advised accordingly -->
<aop:aspectj-autoproxy />
<beans:bean id="loggingAspect" class="xx.yy.aop.aspects.LoggingAspect" />
<beans:bean id="authenticationAspect" class="xx.yy.aop.aspects.AuthenticationAspect" />
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven />
Following is my Aspect:
#Configurable
#Component
#Aspect
public class AuthenticationAspect {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationAspect.class);
#Autowired
private LoginService loginService;
//....
}
Here is my controller using the #Authentication Annotation defined above:
#Controller
#RequestMapping("/user")
public class UsersController {
#Autowired
private UserService userService;
#Authenticate
#RequestMapping(value="/{userId}/profile", method=RequestMethod.GET)
public String displayUser(WebRequest webRequest, #PathVariable("userId") String userId, Model model) {
User user = userService.findUser(Long.valueOf(userId));
model.addAttribute("user", user);
model.addAttribute("AccordionMenuTab","5");
model.addAttribute("selectedLink","profile");
return "profile";
}
I am getting following exception:
Oct 8, 2011 3:12:48 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet appServlet threw exception
java.lang.NullPointerException
at xx.yy.controller.UsersController.displayUser_aroundBody1$advice(UsersController.java:28)
at xx.yy.controller.UsersController.displayUser(UsersController.java:1)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:426)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
See this piece of the documentation:
7.8.3 Configuring AspectJ aspects using Spring IoC
When using AspectJ aspects with Spring applications, it is natural to both want and expect to be able to configure such aspects using Spring. The AspectJ runtime itself is responsible for aspect creation, and the means of configuring the AspectJ created aspects via Spring depends on the AspectJ instantiation model (the 'per-xxx' clause) used by the aspect.
The majority of AspectJ aspects are singleton aspects. Configuration of these aspects is very easy: simply create a bean definition referencing the aspect type as normal, and include the bean attribute 'factory-method="aspectOf"'. This ensures that Spring obtains the aspect instance by asking AspectJ for it rather than trying to create an instance itself. For example:
<bean id="profiler" class="com.xyz.profiler.Profiler"
factory-method="aspectOf" />
For anyone looking for a java based bean configuration, Using java reflections I could archive the same
#Bean
public ExceptionAspectHandler exceptionAspectHandler(){
try
{
//noinspection JavaReflectionMemberAccess
Method method = ExceptionAspectHandler.class.getMethod("aspectOf" );
return (ExceptionAspectHandler) method.invoke(null);
}
catch( IllegalAccessException | InvocationTargetException | NoSuchMethodException e )
{
logger.log( Level.SEVERE, "Error creating bean : ", e );
}
return null;
}
Since the aspectOf() method is not available during compile time we cannot create the bean by just calling the method. That is why XML configuration is able to handle it.
Alternatively simpler approach
#Bean
public ExceptionAspectHandler exceptionAspectHandler()
{
return Aspects.aspectOf( ExceptionAspectHandler.class );
}
This also does work.

Resources