Configuring JNDI URL resources in embedded Tomcat Spring Boot application - spring-boot

I am having a Spring Boot application with embedded Tomcat server. My application does JNDI look up of datasource and config file URL.
I am able to configure JNDI datasource but i am unable to configure JNDI URL in embedded Tomcat. Same application when deployed as WAR to standalone Liberty server is working fine as Liberty is configured properly with JNDI datasource and config file URL.
Also JNDI data sources and URLs are defined in spring config file in a jar of my application which is common and I cannot modify it but only import.
This is how i am trying to configure embedded tomcat
import java.net.URL;
import javax.sql.DataSource;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.ContextResource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.Profile;
#SpringBootApplication(exclude = { JmxAutoConfiguration.class,DataSourceAutoConfiguration.class })
#ImportResource(locations = { "classpath:common-services.xml" })
public class MyApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
#Profile("dev")
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
return new TomcatEmbeddedServletContainerFactory() {
#Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatEmbeddedServletContainer(tomcat);
}
#Override
protected void postProcessContext(Context context) {
ContextResource resource = new ContextResource();
resource.setName("jdbc/myDataSource");
resource.setType(DataSource.class.getName());
resource.setProperty("driverClassName", "oracle.jdbc.OracleDriver");
resource.setProperty("url", "jdbc:oracle:thin:#sadasd");
resource.setProperty("username", "admin");
resource.setProperty("password", "admin");
resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
context.getNamingResources().addResource(resource);
ContextResource queryOnlyDataSourceResource = new ContextResource();
queryOnlyDataSourceResource.setName("jdbc/queryOnlyDataSource");
queryOnlyDataSourceResource.setType(DataSource.class.getName());
queryOnlyDataSourceResource.setProperty("driverClassName", "oracle.jdbc.OracleDriver");
queryOnlyDataSourceResource.setProperty("url", "jdbc:oracle:thin:#osajodoajo");
queryOnlyDataSourceResource.setProperty("username", "admin");
queryOnlyDataSourceResource.setProperty("password", "admin");
queryOnlyDataSourceResource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
context.getNamingResources().addResource(queryOnlyDataSourceResource);
ContextResource dataSourceOrbisExport = new ContextResource();
dataSourceOrbisExport.setName("jdbc/exportDataSource");
dataSourceOrbisExport.setType(DataSource.class.getName());
dataSourceOrbisExport.setProperty("driverClassName", "oracle.jdbc.OracleDriver");
dataSourceOrbisExport.setProperty("url", "jdbc:oracle:thin:#saddalsdkla");
dataSourceOrbisExport.setProperty("username", "admin");
dataSourceOrbisExport.setProperty("password", "admin");
dataSourceOrbisExport.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
context.getNamingResources().addResource(dataSourceOrbisExport);
// File Configuration
ContextResource engineConfigResource = new ContextResource();
evolutionEngineConfigResource.setName("url/engineConfig");
evolutionEngineConfigResource.setType(URL.class.getName()); resource.setProperty("protocol","file");
evolutionEngineConfigResource.setProperty("file", "C:/configuration_DIT/EngineConfig.config");
context.getNamingResources().addResource(engineConfigResource);
ContextResource evolutionPresentationConfigResource = new ContextResource();
presentationConfigResource.setName("url/presentationConfig");
presentationConfigResource.setType(URL.class.getName()); resource.setProperty("protocol","file");
evolutionPresentationConfigResource.setProperty("file", "C:/configuration_DIT/presentation.config");
context.getNamingResources().addResource(presentationConfigResource);
}
};
}
}
This is what error says :
required a bean of type 'java.net.URL' that could not be found.
Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'engineConfig': Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [url/engineConfig] is not bound in this Context. Unable to find [url].

Related

How to enable connection pooling in spring boot embedded tomcat

I have a spring boot application which is not a web application. In this application i have configured embedded tomcat with the help of following bean.
#Bean
public TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory() {
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatWebServer(tomcat);
}
protected void postProcessContext(Context context) {
ContextResource contextResource = new ContextResource();
contextResource.setName("jdbc/BPMDB");
contextResource.setType(DataSource.class.getName());
contextResource.setProperty("driverClassName", env.getProperty("bpm.db.driverClassName"));
contextResource.setProperty("url", env.getProperty("bpm.db.url"));
contextResource.setProperty("username", env.getProperty("bpm.db.username"));
contextResource.setProperty("password", env.getProperty("bpm.db.password"));
context.getNamingResources().addResource(contextResource);
}
};
}
How do i do connection pooling for this embedded tomcat. I am using spring boot 2.x which says hikaricp is the default connection pooling but how to set it into this embedded tomcat.
Does this require to set properties like spring.datasource.hikari.initial-size=15
spring.datasource.hikari.max-wait=20000
but again how boot will know and how will i know that these properties are used.
Thanks.
I have got answer for my problem.
Its simple. We just have to make a DataSource reference and autowire it and mention database related properties along with hikari related properties.
Code is below.
#Autowired
public DataSource dataSource;
Add above to your #Configuration marked class and add following properties to application.properties file.
spring.datasource.driver-class=...
spring.datasource.url=jdbc:oracle:thin:....
spring.datasource.username=..
spring.datasource.password=..
spring.datasource.hikari.initial-size=15
spring.datasource.hikari.max-wait=20000
spring.datasource.hikari.max-active=50
spring.datasource.hikari.max-idle=50
spring.datasource.hikari.min-idle=8
Also i have written a test case to check for hikari connection pool. Below is the code.
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest(
properties = "spring.datasource.type=com.zaxxer.hikari.HikariDataSource",
classes = {ApplicationConfiguration.class,PersistenceJpaContext.class}
)
public class HikariConnectionPoolTest {
#Autowired
private DataSource dataSource;
#Test
public void hikariConnectionPoolIsConfigured() {
assertEquals("com.zaxxer.hikari.HikariDataSource", dataSource.getClass().getName());
}
}

spring-ws .wsdl doesnt contain ObjectFactory.class or jaxb.index

I am following the spring guide to create the client of a web service. Firstly, I create the server and I don't have problems. I generated a jar and executed it. It returns the response.
These are my clases:
InventoryClient.java
public class InventoryClient extends WebServiceGatewaySupport {
public CatalogResponse getCatalog(Catalog cataog) {
Catalog request = new Catalog();
CatalogResponse response = (CatalogResponse) getWebServiceTemplate()
.marshalSendAndReceive("http://localhost:8080/ws/", request,
new SoapActionCallback(
"http://com.uciext.ws.hw5/Catalog"));
return response;
}
}
InventoryConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#Configuration
public class InventoryConfiguration {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this package must match the package in the <generatePackage> specified in
// pom.xml
marshaller.setContextPath("inventory.wsdl");
return marshaller;
}
#Bean
public InventoryClient getCatalog(Jaxb2Marshaller marshaller) {
InventoryClient client = new InventoryClient();
client.setDefaultUri("http://localhost:8080/ws");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
Util.log("---- Application: args[1] "+args[0]);
}
#Bean
void lookup(InventoryClient inventoryClient) {
Catalog catalog = new Catalog();
CatalogResponse response = inventoryClient.getCatalog(catalog);
Util.log("---- Application: date"+response.getReturn().getLastModifiedDate());
}
}
When I execute the jar, I get the following error
Unknown JAXB exception; nested exception is javax.xml.bind.JAXBException: Provider com.sun.xml.internal.bind.v2.ContextFactory could not be instantiated: javax.xml.bind.JAXBException: "inventory.wsdl" doesnt contain ObjectFactory.class or jaxb.index
- with linked exception:
[javax.xml.bind.JAXBException: "inventory.wsdl" doesnt contain ObjectFactory.class or jaxb.index]
When I created the server, I generated the classes from inventory.xsd
Try to make the wsdl package created by jaxb under JAVA Package directly

How to provide custom SSLContext to Netty server on spring boot

How can we configure a custom SSLContext to a spring boot application with Netty server?
From the source code, I see 'reactor.ipc.netty.http.server.HttpServerOptions' which are some server startup options, but I don't find a way to configure them.
Is there any handler through which we can inject our custom SSLContext?
I am looking something similar to this (Spring 5 WebClient using ssl) where WebClient is configured with a custom SSLContext through 'reactor.ipc.netty.http.client.HttpClientOptions'.
Netty can be customized like blow example in spring-boot 2.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
/**
* author : Mohammad Ghoreishi
*/
#Configuration
#ImportResource({"classpath:convert-iban-service.xml", "classpath:config-loader-context.xml", "classpath*:error-resolver.xml"})
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Bean
public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> customizer(){
return new WebServerFactoryCustomizer<NettyReactiveWebServerFactory>() {
#Override
public void customize(NettyReactiveWebServerFactory factory) {
Ssl ssl = new Ssl();
// Your SSL Cusomizations
ssl.setEnabled(true);
ssl.setKeyStore("/path/to/keystore/keystore.jks");
ssl.setKeyAlias("alias");
ssl.setKeyPassword("password");
factory.setSsl(ssl);
factory.addErrorPages(new ErrorPage("/errorPage"));
}
};
}
}

Spring Boot Apache Artemis Embedded JMS Queue Eample

I am trying to set up a simple Spring Boot application that uses an embedded JMS Queue. I am successful with HornetQ but when I try to convert to Artemis I am getting a failure on the ArtemisConnectionFactory. Here is my code that I use for HornetQ. Any help would be appreciative.
package com.comporium.log.server;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import com.comporium.log.server.services.LogListener;
#SpringBootApplication
public class Application {
#Autowired
private ConnectionFactory connectionFactory;
#Autowired
LogListener logListener;
#Bean
public DefaultMessageListenerContainer messageListener() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(this.connectionFactory);
container.setDestinationName("loggerQueue");
container.setMessageListener(logListener);
return container;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
For me your code worked. To test the application I have added a CommandLineRunner which produces a message.
#Bean
CommandLineRunner sendMessage(JmsTemplate jmsTemplate) {
return args -> {
jmsTemplate.convertAndSend("loggerQueue", "Message to Artemis");
};
}
The consumer will consume the message sent to this queue. It it not necessary to declare any properties, but I have defined the following compile time dependencies on my project:
compile('org.springframework.boot:spring-boot-starter-artemis')
compile('org.apache.activemq:artemis-jms-server')

Why do I need to set ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE in ServletContext?

I running an Apache Tomcat server and deploy my application as a WAR file onto it. Inside the application I created a simple REST resource using Apache CXF.
The web.xml only references my listener (ContextListener) class.
In this class I create my application context, and add the CXF servlet.
If I leave out the following line, CXF does not work as expected, although the resource bean is registered ("No service was found.").
Could you please explain what this line does, why it is necessary, and which alternatives exist?
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);
Complete file:
import xxx.resources.DefaultResource;
import org.apache.cxf.jaxrs.spring.SpringComponentScanServer;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
public class ContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.setServletContext(servletContext);
applicationContext.register(SpringComponentScanServer.class);
applicationContext.register(DefaultResource.class);
applicationContext.refresh();
Class<CXFServlet> cxfServletClass = CXFServlet.class;
ServletRegistration.Dynamic servletRegistration = servletContext.addServlet(cxfServletClass.getSimpleName(), cxfServletClass);
servletRegistration.addMapping("/*");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
CXFServlet (which uses Spring) makes use of Spring's WebApplicationContextUtils. This class in turn uses the very same key to retrieve the application context out of the servlet context. In other words, as I am using CXFServlet, I need to use this key to store the context.

Resources