Using Netflix Ribbon without Spring Boot in Legacy Application - java

I work at an application which is using Apache Mina as SFTP Server. The application itself is started as jar and sends rest requests to our backend.
I now want to use Netflix Ribbon without turning the whole application into a spring boot project or spring project in general.
My approach is to access the api directly like in the example:
public class MyClass {
#Autowired
private LoadBalancerClient loadBalancer;
public void doStuff() {
ServiceInstance instance = loadBalancer.choose("stores");
URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
// ... do something with the URI
}
}
Examples in the documentation only show how it is done if configuration is done by spring automatically. However this is not working for me and I cannot get spring to automatically provide the loadbalancer bean.

I solved the problem by "hardcoding" the spring parts:
#Configuration
public class LoadbalancerConfig {
#Bean
public ILoadBalancer loadBalancer() {
BaseLoadBalancer baseLoadBalancer = new BaseLoadBalancer("balancer", rule(), new LoadBalancerStats("balancer"));
baseLoadBalancer.addServers(serverList().getInitialListOfServers());
return baseLoadBalancer;
}
#Bean
public IRule rule() {
return new RandomRule();
}
#Bean
public ServerList<Server> serverList() {
return new StaticServerList<>((new Server("host1", 80)),
new Server("host2", 80));
}
}
Util class for getting bean at later point:
public class BeanUtil implements ApplicationContextAware {
private static final Logger log = LogManager.getLogger(BeanUtil.class);
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
applicationContext = ctx;
}
public static <T> T getBean(Class<T> beanClass) {
return applicationContext.getBean(beanClass);
}
}
I initiate them through a xml file:
<context:component-scan base-package="package.of.loadbalancerconfig" />
<bean id="applicationContextProvider" lazy-init="false" class="my.package.BeanUtil" />
Dont forget to create your applicationContext at initialization:
ApplicationContext context = new FileSystemXmlApplicationContext("file:/path/to/beans.xml");
Now I could get the loadbalancer and the instances:
if (loadBalancer == null) {
loadBalancer = BeanUtil.getBean(ILoadBalancer.class);
}
Server instance = loadBalancer.chooseServer("balancer");
URI uri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
I'm sure there is a more elegant way, but it worked for me.

Related

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.

Java Spring-boot - How to use #Autowired with #ServerEndpoint?

I know there are lot of questions on this topic. I have read the spring boot doc and all of the solutions here. According spring boot doc, #ServerEndpoint is a Javax annotation and #Autowired components are spring-boot managed. These two cannot be used together. The solution to this would be to add SpringConfigurator as configurator of the ServerEndpoint. When I tried this I do get the following error:
Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?
There is no example in the spring-boot websocket page to use ContextLoaderListener. How can use ContextLoaderListener so that components can be injected into #ServerEndpoint annotated controllers?
The following is my code.
Websocket controller
#ServerEndpoint(value = "/call-stream", configurator = SpringConfigurator.class)
public class CallStreamWebSocketController
{
#Autowired
private IntelligentResponseService responseServiceFacade;
// Other methods
}
Websocket configurations
#Configuration
public class WebSocketConfiguration
{
#Bean
public CallStreamWebSocketController callStreamWebSocketController()
{
return new CallStreamWebSocketController();
}
#Bean
public ServerEndpointExporter serverEndpointExporter()
{
return new ServerEndpointExporter();
}
}
Edit:
This has been tagged as a duplicate of this question. I have tried the solution specified in the answers. The solution is to add SpringConfigurator as configurator of the #ServerEndpoint. After adding this I still do get the error mentioned in the details.
After some research I found a way to force spring-boot to inject a component into an externally managed/instantiated class.
1) Add a generic method to your class extending ApplicationContextAware to return a bean.
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContext.context = context;
}
public ApplicationContext getApplicationContext() {
return context;
}
// Generic method to return a beanClass
public static <T> T getBean(Class<T> beanClass)
{
return context.getBean(beanClass);
}
}
2) Use this method to initialize the class object you want to be injected
private IntelligentResponseService responseServiceFacade = SpringContext.getBean(IntelligentResponseService.class);
So after the above changes my websocket controller would look like this
#ServerEndpoint(value = "/call-stream", configurator = SpringConfigurator.class)
public class CallStreamWebSocketController
{
private IntelligentResponseService responseServiceFacade = SpringContext.getBean(IntelligentResponseService.class);
// Other methods
}

Tomcat hangs shutting down with Spring Integration Java DSL

I'm having an issue where trying to gracefully shutdown Tomcat (8) never finishes, due to what appears to be DefaultMessageListenerContainer being blocked (or looping) indefinitely.
I've been googling around for solutions, but anything similar I've found hasn't worked. This includes (but is not limited to):
Using configureListenerContainer() to set the taskExecutor of the container
Using Messages.queue() instead of Messages.direct()
Wrapping the ActiveMQConnectionFactory in a CachingConnectionFactory
A simple Servlet 3.0 example:
compile 'org.springframework.integration:spring-integration-core:4.3.6.RELEASE'
compile 'org.springframework.integration:spring-integration-jms:4.3.6.RELEASE'
compile 'org.springframework.integration:spring-integration-java-dsl:1.2.1.RELEASE'
Initializer:
public class ExampleWebApp implements WebApplicationInitializer {
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
final AnnotationConfigWebApplicationContext springContext = new AnnotationConfigWebApplicationContext();
springContext.register(ExampleConfig.class);
servletContext.addListener(new ContextLoaderListener(springContext));
final ServletRegistration.Dynamic registration = servletContext.addServlet("example", new HttpRequestHandlerServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/status");
}
}
Configuration:
#Configuration
#EnableIntegration
public class ExampleConfig {
#Bean
public ConnectionFactory connectionFactory() {
final ActiveMQConnectionFactory mqConnectionFactory = new ActiveMQConnectionFactory();
mqConnectionFactory.setBrokerURL("tcp://host:port");
mqConnectionFactory.setUserName("----");
mqConnectionFactory.setPassword("----");
return mqConnectionFactory;
}
#Bean
public Queue testQueue() {
return new ActiveMQQueue("test.queue");
}
#Bean
public MessageChannel testReceiveChannel() {
return MessageChannels.direct().get();
}
#Bean
public IntegrationFlow pushMessageInboundFlow() {
return IntegrationFlows
.from(Jms.messageDrivenChannelAdapter(connectionFactory())
.destination(testQueue()))
.log()
.transform(new JsonToObjectTransformer(TestMessageObject.class))
.channel(testReceiveChannel())
.get();
}
/** Example message object */
public static class TestMessageObject {
private String text;
public String getText() {
return text;
}
public void setText(final String text) {
this.text = text;
}
}
}
If I try and stop this via the catalina.sh script (for example, pressing "stop" in Intellij", it never finishes existing. So far the only way I've been able to get shutdown to finish is by "manually" destroying the JmsMessageAdapters on shutdown, via a little helper class:
public class JmsMessageListenerContainerLifecycleManager {
private static final Logger LOG = LoggerFactory.getLogger(JmsMessageListenerContainerLifecycleManager.class);
#Autowired
private List<IntegrationFlow> mIntegrationFlows;
#PreDestroy
public void shutdownJmsAdapters() throws Exception {
LOG.info("Checking {} integration flows for JMS message adapters", mIntegrationFlows.size());
for (IntegrationFlow flow : mIntegrationFlows) {
if (flow instanceof StandardIntegrationFlow) {
final StandardIntegrationFlow standardFlow = (StandardIntegrationFlow) flow;
for (Object component : standardFlow.getIntegrationComponents()) {
if (component instanceof JmsMessageDrivenChannelAdapter) {
final JmsMessageDrivenChannelAdapter adapter = (JmsMessageDrivenChannelAdapter) component;
LOG.info("Destroying JMS adapter {}", adapter.getComponentName());
adapter.destroy();
}
}
}
}
}
}
And while that works, it definitely feels like the wrong solution.
Previously I was using XML configuration of spring-integration, and I did not have this problem. What am I missing?
Ugh! This is definitely a bug. And looks like you workaround it properly.
Although consider to destroy any DisposableBean there.
I'm adding the fix to the Spring Integration Java DSL. We are going to release the next 1.2.2 just after Spring Integration 4.3.9.
The Spring Integration 5.0 will have a fix in its M3 release tomorrow.

Not able to load Application Context in Spring Boot app

I am working on a Spring Boot application wherein I am using that application to expose a SOAP webservice. I am using Apache CFX framework for SOAP impl in Spring boot app. I am using Annotation based approach.
I am facing issue in setting the Application Context from the Spring Boot Configuration file in one of the Beans. Below is my code.
#SpringBootApplication
#ComponentScan("com.test")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The configuration file is as below.
#Configuration
public class WebServiceConfiguration {
//All individual bean definitions should go here
#Autowired
ApplicationContext appContext;
#Bean
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
#Bean(name="IValidator")
public IValidator getValidator(){
return new Validator();
}
#Bean(name="SOAPprocessImpl")
public IPSoap getService() {
return new SOAPprocessImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), getService());
endpoint.publish("/WS_1.0");
endpoint.setWsdlLocation("process.wsdl");
return endpoint;
}
Now I have the bean SOAPprocessImpl implementation in which I need to get the Application Context so that I can get handle to the Validator bean. I have declared SOAPprocessImpl as a bean in the configuraton file. The code is as below
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap, ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
context = ac;
}
private static final Logger logger = Logger.getLogger(SOAPprocessImpl.class.getName());
private IValidator validator = (IValidator) context.getBean("IValidator"); // context is NULL here
public IRResponse GetBalance(TSSearchParams SearchParams) {
// Some processing logic
}
}
So the issue is that when I run the boot application by deploying to the embedded Tomcat then the Application Context is not getting set in the SOAPprocessImpl class even after implementing the ApplicationContextAware. I also tried Autowiring but that also is not working.
Strangely I tried to see if I can get the ApplicationContext in the Configuration file where all the bean are defined. Here it is getting setting properly.
Can anyone help me how to solve this issue. I am new to Spring Boot and may have missed some configutaion. Thanks in advance.
Option(1): To fix the issue, you need to use #Configuration to register your SOAPprocessImpl bean to the Spring container as shown below so that ApplicationContext object can be injected :
#Configuration
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap, ApplicationContextAware {
private static ApplicationContext context;
private IValidator validator;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
SOAPprocessImpl.context = ac;
}
#PostConstruct//use PostConstruct
public void init() {
validator = (IValidator) context.getBean("IValidator");
}
//add your current code
}
The important point is that you can't use the context object until the bean is prepared by the container, so you need to use #PostConstruct method as shown above to initialise your variables.
Option2 (recommended):
The best approach is that you can use #Autowired to inject IValidator object into SOAPprocessImpl as shown below so that you don't need your SOAPprocessImpl bean to be aware of ApplicationContextAware. Spring container will inject the instance for the implementation provided for the IValidator class (provided it is under the packages of #Componentscan).
#Component
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap {
private static final Logger logger = Logger.getLogger(SOAPprocessImpl.class.getName());
#Autowired //spring directly injects this object
private IValidator validator;
public IRResponse GetBalance(TSSearchParams SearchParams) {
// Some processing logic
}
}

NullPointerException on service spring

I'm new to Spring so I'm just trying to understand how it works. I've developed a simple servletprojectusing spring to manage hibernate framework.
I have a service
#Service("service")
#Transactional
public class CdServiceImpl {
#Autowired
private HibernateUtility hibernateutility;
public int saveCd(CD cd) {
return hibernateutility.saveCd(cd);
}
public List getCd(String searchedCd) {
return hibernateutility.getCd(searchedCd);
}
public List getAllCd() {
return hibernateutility.getAllCd();
}
public void deleteCd(int id) {
hibernateutility.deleteCd(id);
}
public User getUser(String username, String password) {
return hibernateutility.getUser(username, password);
}
}
And then I use it in the servlet
context.scan("it.project");
context.refresh();
CdServiceImpl service = (CdServiceImpl) context.getBean("service");
context.register(ApplicationContextConfig.class);
context.refresh();
1) It works but I have two question. It is the right way to work?
2) I've tried to set a field in the servlet like:
#Autowired
private CdServiceImpl service
and then I remove the context.scan ecc part and it gave me nullpointerexception. Why?
Doing that I also defined a new bean
#Bean
public CdServiceImpl getCdServiceImpl() {
return new CdServiceImpl();
}
Why it doesn't work? I know that maybe this is a noob question but I'm tryingto figure out how spring works
Basically as soon as you start doing things like new **ApplicationContext you need to scratch your head, take 2 steps away from the keyboard and think if you really want to do this. In 99% of the cases this isn't what you want or at least should do.
Instead have the ContextLoaderListener load your configuration. Assuming you don't have a web.xml use the the AbstractContextLoaderInitializer base class.
public ApplicationInitializer extends AbstractContextLoaderInitializer {
protected WebApplicationContext createApplicationContext() {
return new AnnotationConfigWebApplicationContext(ApplicationContextConfig.class);
}
}
Note: As this is for bootstrapping your application you need to create the the context. You could also use AbstractAnnotationConfigDispatcherServletInitializer which eliminates this need, but also creates a DispatcherServlet which you don't need.
Now that your configuration is automatically loaded you can use the WebApplicationContextUtils to get the context to do the lookup. Do this in the init method of your servlet.
public class YourServlet extends GenericServlet {
private CdServiceImpl service;
public void init() throws ServletException {
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
service = ctx.getBean(CdServiceImpl.class);
}
}
Now you initialize the bean once and don't need to handle anything anymore. You could also use #Autowired and manuallly have it injected.
public class YourServlet extends GenericServlet {
#Autowired
private CdServiceImpl service;
public void init() throws ServletException {
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
ctx.getAutowireCapableBeanFactory().autowireBean(this);
}
}

Categories

Resources