#Autowired using the wrong implementation despite #ConditionalOnProperty - java

I followed a tutorial I found online to use #ConditionalOnProperty with #Bean to choose an implementation for #Autowired.
For completeness (though it isn't relevant to the issue), the purpose of this is to choose an "ICE Server List" provider - either as configured locally in a config file or using the Twilio API to get a list of STUN and TURN servers for WebRTC.
Here is the relevant code:
application.properties
ice.provider = twilio
Controller.java
#ComponentScan
#RestController
#EnableAutoConfiguration
#EnableWebSocket
public class Controller {
#Autowired
private IceProvider iceProvider;
#Bean
#ConditionalOnProperty(name = "ice.provider", havingValue = "local")
public IceProvider localIceProvider() {
logger.info("Creating ICE server provider - LOCAL");
return new LocalIceProvider();
}
#Bean
#ConditionalOnProperty(name = "ice.provider", havingValue = "twilio")
public IceProvider twilioIceProvider() {
logger.info("Creating ICE server provider - TWILIO");
return new TwilioIceProvider();
}
}
IceProvider.java
public interface IceProvider {
List<ICEServer> getIceServers(String username);
}
LocalIceProvider.java
#Component
public class LocalIceProvider implements IceProvider {
private final static Logger logger = Logger.getLogger(LocalIceProvider.class);
#PostConstruct
void init() {
logger.info("Reading local ICE Server list config file");
// ...
}
#Override
public List<ICEServer> getIceServers(String username) {
logger.info("Returning locally configured ICE servers");
// ...
}
}
TwilioIceProvider.java
#Component
public class TwilioIceProvider implements IceProvider {
private final static Logger logger = Logger.getLogger(TwilioIceProvider.class);
#Override
public List<ICEServer> getIceServers(String username) {
logger.info("Doing Twilio API call and returning result");
// ...
}
}
Despite the application.properties setting to use the twilio provider, the application is using the local provider. However, only the twilioIceProvider factory method is being called, not the localIceProvider method.
Here is a snippet from the logs:
On application start
Both the #PostConstruct method of LocalIceProvider and the factory method of TwilioIceProvider are called, but not the factory method of LocalIceProvider
...
[2017-12-31 21:11:53 INFO ] [localhost-startStop-1] [ice.LocalIceProvider] Reading local ICE Server list config file
...
[2017-12-31 21:11:55 INFO ] [main] [com.example.Controller] Creating ICE server provider - TWILIO
...
On a request from the client that calls iceProvider.getIceServers()
[2017-12-31 21:14:40 INFO ] [http-nio-9083-exec-5] [ice.LocalIceProvider] Returning locally configured ICE servers
Since only the twilioIceProvider factory method is being called and the localIceProvider method is not, why is the LocalIceProvider being #Autowired?

Remove the #Component annotations from your IceProvider implementations. You are already declaring them as beans using #Bean on the bean definition methods.

Related

How to delay Spring Bean Initialization based on variable

My problem is that I need to get an authentication token before I make a call to an API. The api must be called on start up of application. However I am having trouble in that both calls are made at the same time thus creating an error of not having the authentication token before making the api call.
I basically need the tokenUtilityClass to create the token before instantiating the Paypal class. I have tried the #Preconstruct and the #Lazy annotation but neither are working for me.
I have a boolean value of validToken which returns true once the authentication token has been created.
This is what my Springboot configuration file looks like
#Autowired
private TokenUtilityClass tokenUtilityClass;
#Bean ResourceConfig resourceConfig() {
return new ResourceConfig().registerClasses(Version1Api.class); }
#PostConstruct
public void postConstruct() {
tokenUtilityClass.tokenTimer();
}
#DependsOn("TokenUtilityClass")
#ConditionalOnProperty(name ="tokenUtilityClass.validToken", havingValue ="true")
#Lazy
public Paypal eventPublisherBean() {
return new Paypal();
}
Would anyone have any ideas about initializing the Paypal class only after the authentication token has been generated.
All help would be appreciated
What you declared cannot work :
#DependsOn("TokenUtilityClass")
#ConditionalOnProperty(name ="tokenUtilityClass.validToken", havingValue ="true")
#Lazy
because tokenUtilityClass.validToken is not a property but a bean method while
ConditionalOnProperty expects to a property.
Would anyone have any ideas about initializing the Paypal class only
after the authentication token has been generated.
Concretely, you cannot achieve it straightly with Spring because beans are instantiated as soon as these are required by other beans in their dependencies.
But declaring just that :
#Bean
#Lazy
public Paypal eventPublisherBean() {
return new Paypal();
}
could work if that bean is never used as a eager loaded dependency of other beans but rather required at the good time for you : after the token was retrieved.
To achieve that you have two ways :
1) don't use dependency injection for PayPal instance but use exclusively bean factory.
Not tested but it sounds conform to the #Lazy javadoc :
If this annotation is not present on a #Component or #Bean definition,
eager initialization will occur. If present and set to true, the #Bean
or #Component will not be initialized until referenced by another bean
or explicitly retrieved from the enclosing BeanFactory.
So inject BeanFactory in the bean responsible to get the token and use that factoryto initialize the bean after the token was retrieved.
Something like that :
#Service
public class TokenUtility {
private BeanFactory factory;
public TokenUtility(BeanFactory factory){
this.factory = factory;
}
public Token retrieveToken(){
// call service to get the token
//...
// init the bean now -(it will also invoke its #PostConstruct method)
PayPal payPal = beanFactory.getBean(PayPal.class);
}
}
2) A straighter alternative to the BeanFactory is declaring the bean as a lazy dependency :
#Service
public class TokenUtility {
#Lazy
#Autowired
Paypal paypal;
public Token retrieveToken(){
// call service to get the token
//...
// init the bean by invoking any method on that
paypal.init();
}
}
I created just now a very basic project (Java 11) to test that :
https://github.com/ebundy/lazy-init-spring-example/.
When you execute spring-boot:run you should get :
2020-02-26 09:38:05.883 INFO 7002 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 682 ms
TokenUtility init with status code=200
PayPal init
You are looking for #Order annotation.
#Component
#Order(1)
public class First {
#PostConstruct
public void init() {
System.out.println("first");
}
}
#Component
#Order(2)
public class Second {
#PostConstruct
public void init() {
System.out.println("second");
}
}
#Order works with both types of declarations: #Component/#Service/#Repository/#Controller and with #Bean
However, if you have a clear dependency of one bean on the other bean, then use #DependsOn annotation.
#Component("first")
public class First {
public First() { // inject dependencies here if any
System.out.println("The very first thing")
}
#PostConstruct
public void init() {
System.out.println("first");
}
}
#Component
#DependsOn({"first"})
public class Second {
#PostConstruct
public void init() {
System.out.println("second");
}
}
You can find more info here
It looks like you are sharing the code inside class with #Configuration annotation. It seems you mark TokenUtilityClass with #Component (or similar) annotation.
The issue with that is that #PostConstruct is connected to your Configuration class, not to TokenUtilityClass. I suggest moving #PostConstruct method to TokenUtilityClass.
You can just have token refreshing functionality and make sure token exists upon creating another bean.
#Component
class TokenUtilityClass {
private String freshToken;
private Instant freshUntil;
public String getToken() {
if (freshUntil != null && Instant.now().isBefore(freshUntil)) {
refreshToken();
}
return freshToken;
}
public void refreshToken() {
// do something
}
}
#Configuration
class ConfigurationClass {
#Bean public Paypal eventPublisherBean(TokenUtilityClass tokenUtilityClass) {
String token = tokenUtilityClass.getToken();
// do something with token and return your bean
return new Paypal();
}
}

how to override custom auto-configurations?

I have made a auto-configuration class for my project to connect to AWS Sqs. This class is working fine but when I try to override auto-configuration functionality, I am getting autowire error from calling code. Please guide if my implementation of auto-configuration class is right?
I tried different #Conditional annotations to find the solution but its not working out.
#Configuration
#ConditionalOnClass(AwsSqsAsyncClient.class)
public class AwsSqsAsyncAutoConfiguration {
#Configuration
#ConditionalOnProperty(name = "aws.sqs.queue-type", havingValue = "fifo")
#EnableConfigurationProperties(AwsSqsProperties.class)
static class AwsFifoSqsAsyncAutoConfigurationBuilder{
private final AwsSqsProperties awsSqsProperties; //another class defined by me for properties
#Inject
public AwsFifoSqsAsyncAutoConfigurationBuilder(AwsSqsProperties awsSqsProperties) {
this.awsSqsProperties = awsSqsProperties;
}
#Bean
#ConditionalOnMissingBean
public AwsSqsAsyncClient fifoAsyncClient() {
return AwsSqsFactory.createAwsSqsAsyncClient(AwsSqsMessageRequestFactory
.createAwsSqsFifoRequestFactory("producer-application-name", awsSqsProperties.getQueueUrl()),
awsSqsProperties.getAccessKey(), awsSqsProperties.getSecretKey());
}
}
}
This is where I am trying to override auto-configuration functionality
#Configuration
public class AmazonSQSConfig {
#Bean
public AwsSqsAsyncClient amazonSqsAsyncClient(){
return AwsSqsFactory
.createAwsSqsAsyncClient(AwsSqsMessageRequestFactory
.createAwsSqsFifoRequestFactory("someother-producer-application-name",
"some amazon url"), "access key",
"secret key");
}
}
calling code, this is where I am trying to autowire and getting error
"Could not autowire. there is more than one bean of type AwsSqsAsyncClient
Beans: amazonSqsAsyncClient
and fifoAsyncClient "
private final AwsSqsAsyncClient awsSqsClient;
#Autowired
public SQSPublisherImpl(AwsSqsAsyncClient awsSqsClient) {
this.awsSqsClient = awsSqsClient;
}
Spring boot factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.aws.starter.sqs.AwsSqsAsyncAutoConfiguration
I expect autoconfiguration to get disabled when I define my bean for AwsSqsAsyncClient in calling code

Apache CXF Logging Interceptors Not Triggered on Weblogic on Spring Boot App

I'm using Apache CXF to implement Bottom Up Soap WS on a Spring Boot application. Everything was working fine until deployed on Weblogic (12.2.1.3.0). The issue is that the LoggingInterceptors that I'm trying to use are not being exceuted at all.
First problem was acctually a nullpointer on a #Autowired injection on the WebService implementation class. To fix that I use some workaround that I founded here on StackO. Appears that weblogic start separated context loaders, one for spring and all the beans and one for CXF. So when the webservice impl class is called via wsdl url the injection are not done. After fixing this issue, the WS works fine, but the logging interceptors added via spring boot configuration are not triggered. And one important feature of the application is the ability to store the soap request response on the database. So it's important that the Interceptors chain works.
The Spring Boot config class:
#Configuration
public class LmsReloadCXFWSConfig implements ServletContextInitializer{
private static WebApplicationContext webApplicationContext;
public static WebApplicationContext getCurrentWebApplicationContext() {
return webApplicationContext;
}
#SuppressWarnings({ "rawtypes", "unchecked" })
#Bean
public ServletRegistrationBean servletRegistration() {
return new ServletRegistrationBean(new CXFServlet(), "/*");
}
#Bean
public LoggingInInterceptor logInInterceptor() {
return new LmsReloadWSInboundInterceptor();
}
#Bean
public LoggingOutInterceptor logOutInterceptor() {
return new LmsReloadWSOutboundInterceptor();
}
#Bean(name=Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus springBus = new SpringBus();
return springBus;
}
#Bean
public LmsReloadWebService lmsReloadWebService() {
LmsReloadWebService lmsReloadWebService = new LmsReloadWebServiceImpl();
return lmsReloadWebService;
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), lmsReloadWebService());
endpoint.getInInterceptors().add(logInInterceptor());
endpoint.getOutInterceptors().add(logOutInterceptor());
endpoint.publish("/Soap");
return endpoint;
}
}
Both are Custom Interceptors that extends LoggingInInterceptor and LoggingOutInterceptor:
public class LmsReloadWSInboundInterceptor extends LoggingInInterceptor{
...
}
public class LmsReloadWSOutboundInterceptor extends LoggingOutInterceptor{
...
}
The Web Service interface and implementation:
#WebService(name = "LmsServiceInterface", targetNamespace=LmsReloadUtil.LMSRELOAD_NAMESPACE_ORI)
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public interface LmsReloadWebService {
...
}
#WebService(portName = "LmsServicePort", serviceName = "Soap", targetNamespace = LmsReloadUtil.LMSRELOAD_NAMESPACE_ORI, endpointInterface = "com.dell.lms.reload.ws.LmsReloadWebService")
public class LmsReloadWebServiceImpl implements LmsReloadWebService {
#Autowired
private LmsReloadService lmsReloadService;
#Autowired
private LmsReloadLoggingService lmsReloadLoggingService;
public LmsReloadWebServiceImpl() {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
WebApplicationContext currentContext = LmsReloadCXFWSConfig.getCurrentWebApplicationContext();
bpp.setBeanFactory(currentContext.getAutowireCapableBeanFactory());
bpp.processInjection(this);
}
...
}
The CXF version on my pom.xml is 3.2.7
What I need to do is to get those interceptors working so I can save the request and response on the database. Again, the application works fine and coomplete when executed useing the spring boot tomcat started in Eclipse. And since the issue with the Autowired on the WebService Impl Class, I think is something related to the way weblogic depoloy the application. Distinct context loaders.
Being investigating this problem during the last week, found a lot of possible solution, tried all but with no success.

Can Spring 3 supports WebSocket [duplicate]

I'm using Spring 3.2.5 without full new JSR-356 WebSockets support.
I would like to have singleton-bean reference in my #ServerEndpoint WebSocket server, which is instantiated by servlet container itself, not in Spring context.
What is the clean way to do it?
My current solution: I made #Service singleton bean with instance in static field:
#Service
public class WebSocketSupportBean {
private volatile static WebSocketSupportBean instance = null;
public static WebSocketSupportBean getInstance() {
return instance;
}
public WebSocketSupportBean() {
instance = this;
}
and just getting it in #ServerEndpoint by static method, disconnecting user if null returned (if bean not jet created during server startup but user connects):
You can setup websockets with spring framework 3.x
I developed a small proof-of-concept application to demonstrate how, based on Rossen Stoyanchev's SpringConfiguration released with spring-core 4.0.
The application sets up a websocket server endpoint with uri /wstest which will use a #Autowired spring bean to select a greeting word and reply to a websocket message.
The websocket connection is initiated and messages sent by an html page (index.html) running in a browser that supports websockets.
The Endpoint registration is made by a ServletContextListener at context initialization and when the endpoint is instantiated it will be wired with spring:
#WebListener
public class MyApplication implements ServletContextListener {
private final static String SERVER_CONTAINER_ATTRIBUTE = "javax.websocket.server.ServerContainer";
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext container = sce.getServletContext();
final ServerContainer serverContainer = (ServerContainer) container.getAttribute(SERVER_CONTAINER_ATTRIBUTE);
try {
serverContainer.addEndpoint(new MyEndpointConfig(MyEndpoint.class, "/wstest"));
} catch (DeploymentException e) {
e.printStackTrace();
}
}
}
And the Endpoint is:
#Component
public class MyEndpoint extends Endpoint {
#Autowired
MyService myService;
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MyMessageHandler(session));
}
class MyMessageHandler implements MessageHandler.Whole<String> {
final Session session;
public MyMessageHandler(Session session) {
this.session = session;
}
#Override
public void onMessage(String message) {
try {
String greeting = myService.getGreeting();
session.getBasicRemote().sendText(greeting + ", got your message (" + message + "). Thanks ! (session: " + session.getId() + ")");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Checkout the full source and ready to run example on my Github page.
You have to add bean definition in the configuration of spring.
The solution i found to integrate JSR 356 websocket #ServerEndpoint is to turn off the Servlet container's scan for WebSocket endpoints by spring which can be done by registering #Bean in your Spring Configuration. By this spring not overrides normal JSR 356 websocket by spring STOMP websocket which is the part of the websocket.
#ServerEndpoint(value="/chatMessage")
public class ChatEndpoint{
// Normal websocket body goes here.
}
Adding Beans in your Configuration as:
#Configuration
public class WebsocketConfig{
#Bean
public ChatEndpoint chatEndpoint(){
return new ChatEndpoint();
}
// main one is ServerEndpointExporter which prevents Servlet container's scan for WebSocket
#Bean
public ServerEndpointExporter endpointExporter(){
return new ServerEndpointExporter();
}
}
This all done for you. But you should remove configurator = SpringConfigurator.class from #ServerEndpoint.
I am using Spring Websocket 4.0.0 and it works fine.
You can also see this Link.
If you alright then follow this Link also for concept.
Note that, Normally you should make websocket configuration separately from the main configuration of your spring.
Try
#ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)
And add maven dependency
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
You can make your #ServerEndpoint object extend SpringBeanAutowiringSupport. Then just make it aware of beans that gets constructed within a Spring-based web application this way:
#PostConstruct
public void init() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
This way #Autowired annotation will worl correctly:
#Autowired MyService myService;
try this,it works for me
#Component
#ServerEndpoint(value = "/instantMessageServer",configurator = SpringConfigurator.class)
public class InstantMessageServer{
private static IChatService chatService;
#Autowired
public InstantMessageServer(IChatService chatService){
this.chatService = chatService;
}
public InstantMessageServer(){}
}
I found this solution on https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support
but there is one more glitch,the class annotated with #ServerEndpoint cant acquire httpsession with SpringConfigurator,there is no a override of method modifyhandler in it.Maybe we create a seperate Configurator extends SpringConfigurator and override that method would be a workaroud.
It is better to build a real-time web application with spring-websocket and messaging api,I think.
public class ModifiedServerEndpointConfigurator extends SpringConfigurator{
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
super.modifyHandshake(sec, request, response);
}
}

Service not being correctly autowired - annotation-based configuration

I'm using annotation-based configuration - no XML at all.
I've configured everything, but can't figure out why OrderService is not being autowired and continues to be null. The class below at the very bottom is the one that shows the actual problem. The other classes are all my configuration.
I do have log4j on this application but am inexperienced with it. Is there a way I can log what packages/classes are being scanned to help determine why this isn't working?
OrderService
#Service
public class OrderService extends GenericService<OrderDAO, Order> {
#Autowired
OrderDAO dao;
}
Services config
#Configuration
public class Config {
#Bean
public OrderService orderService() {
return new OrderService();
}
}
Main Config
#Configuration
#ComponentScan(basePackages = {
"com.production.api",
//todo: may not need the rest of these
"com.production.api.dao",
"com.production.api.models",
"com.production.api.requests",
"com.production.api.requests.job",
"com.production.api.resources",
"com.production.api.resources.job",
"com.production.api.services"
})
#Import({
com.production.api.services.Config.class,
com.production.api.dao.Config.class
})
#PropertySource(value= "classpath:/META-INF/application.properties")
#EnableTransactionManagement
public class Config {
Main.java
public static void main(String[] args) throws IOException {
//process annotation configuration
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
HttpServer httpServer = startServer();
System.out.println(String.format("Jersey app started with WADL available at " + "%sapplication.wadl\nTry out %shelloworld\nHit enter to stop it...", BASE_URI, BASE_URI));
System.in.read();
httpServer.stop();
}
Where the problem is...
#Component
public class NewJobRequestHandler {
public static Logger logger = Logger.getLogger(Logger.class.getName());
//#todo Why isn't this autowiring?
#Autowired
OrderService orderService;
public void instantiateOrderService() {
//#todo remove this manual wiring
orderService = (OrderService) ApplicationContextProvider.getApplicationContext().getBean("orderService");
}
public AuthenticatedApiResponse getResponse(AuthenticatedRequest<NewJobRequest> request) {
instantiateOrderService();
The problem here is that your Spring configuration and context is separated from your Jersey/Grizzly stack. You are expecting Jersey to be able to get beans from Spring, but it has no knowledge that Spring exists, its annotations don't mean anything to it.
You need to replace your Jersey Servlet with Spring's Jersey Servlet and add a ContextLoaderListener. Take a look at this example on how to wire Jersey and Spring. I'm not sure how Grizzly works, but if it's like any other servlet container, this should work.
For log4j, you can set your root logger level to INFO or DEBUG and you will get all sorts of log statements from Spring.

Categories

Resources