Spring boot war file does not work on tomcat - java

I added spring boot to a existing webapp. When i run the command
java -jar -Denvironment.type=dev myfile.war
Every things goes fine. But if I deploy on tomcat, for some reason a get a very big exception.
Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath.
I am using mongodb and I do not have any datasource configured on my application context. I also extended SpringBootServletInitializer
#SpringBootApplication
public class AdminApp extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(AdminApp.class);
}
public static void main(String[] args) {
SpringApplication.run(AdminApp.class, args);
}
}
Any clue what can it be?
My properties file
database.url=localhost
database.port=27017
database.name=dbname
database.username=admin
database.password=admin
Update: I also have this class the says which property file should be used.
#Configuration
#PropertySource("classpath:application-${environment.type}.properties")
public class PropertyWithJavaConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}

The error is thrown in DataSourceProperties.getDriverClassName() method. Find below the source code of the same from spring distribution:
if (!StringUtils.hasText(driverClassName)) {
throw new BeanCreationException(
"Cannot determine embedded database driver class for database type "
+ this.embeddedDatabaseConnection
+ ". If you want an embedded "
+ "database please put a supported one on the classpath.");
}
Spring throws this error when spring.datasource.driverClassName property is empty. So to fix this error, make sure that the application.properties is in the classpath.

So, after digging a lot on the dependencies I noticed that I had spring-orm and spring-jdbc even though I was not using. I removed them and everything worked fine for embedded and local tomcat.
But I still can not understand why before worked just for embedded tomcat.

Related

Spring Boot YAML binding: fail when using yml extension

i cannot start my spring-boot application, because an exception with the following messagge is thrown"Error creating bean with name 'application': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'tracer.datadog' in value
The reason is that spring-boot cannot find the "tracer.datadog" prop specificied on the application class:
#SpringBootApplication
#EnableMongoAuditing
public class Application {
#Value("${tracer.datadog}")
private boolean datadog;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#PostConstruct
private void jaegerTracer() {
if (!datadog) {
GlobalTracer.registerIfAbsent(Configuration.fromEnv().getTracer());
}
}
}
But that property is correctly defined over the application.yml file:
tracer:
datadog: true
However, changing the extension to .YAML spring-boot "find" the file and the application starts fine.
Does anyone can tell me how can i do to solve this problem and use the .yml extension? using the .yaml extension is not a possibility because of company standards

Could not resolve placeholder 'spring.profiles.active' in value "classpath:/ldap-${spring.profiles.active}.properties"

I am trying read ldap properties from a ldap-TEST.properties file
and trying to bind it to a java config class.for that i had specified
#PropertSource and defined a static Bean for propertysourcesplaceholderconfigurer.
still i am getting the Could not resolve placeholder spring.profiles.active in value classpath:/ldap-${spring.profiles.active}.properties below are project files please help me
#Configuration
#PropertySource("classpath:/ldap-${spring.profiles.active}.properties")
public class LdapConfig {
#Autowired
Environment env;
#Bean
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(env.getRequiredProperty("ldap.url"));
contextSource.setBase(env.getRequiredProperty("ldap.base"));
contextSource.setUserDn(env.getRequiredProperty("ldap.userDn"));
contextSource.setPassword(env.getRequiredProperty("ldap.password"));
contextSource.afterPropertiesSet();
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSource());
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
//ldap-TEST.properties file
ldap.base=dc=example,dc=com
ldap.password=password
ldap.port=839
ldap.userDn=cn=read-only-admin,dc=example,dc=com
ldap.url=ldap://ldap.forumsys.com:389
my main application
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You can not use properties like ${spring.profiles.active} inside string value of Type annotation in spring. such properties would be injected into annotations like #Value which are for properties or methods.
The value behind spring.profiles.active is actually an array. So even if the value was correctly expanded, there would be corner cases when it wouldn't work the way you want.
It'd be nice if the paths configured via #PropertySource would work the same way the application.properties|yml does, but that is not the case at the moment (there is an active issue on GitHub about that). So alternatives have to be considered:
The simplest alternative would be to use the conventional files names application.properties|yml and application-{profile}.properties|yml. I do not see any good reason not to do it, but I do not know your project requirements so...
A bit more complicated, get the configured profiles using Java code, and configure the Spring environment programmatically. See this SO answer for more details.
If you are running on local machine, then Just change property file name application-default.property for temporary immediate work.
For permanent solution you have to check that your project is running on docker or not:
if running on docker then use below command:
mvn clean package -DskipTests=true && sudo docker build
sudo docker run -d -e spring.profiles.active="local" -e <other key and value of bootstrap.property file>
else if running on cloud server then follow below steps:
in bootstrap.property file put spring.profiles.active=local
rename application file to application-local.properties
you can also refer: 2.3.3. Profile Specific Files part at configuration document

Unable to add external JDBC jar file to Spring Boot, despite adding it to application.properties

I tried following the instructions mentioned here to add a driver for the SAP HANA database. The driver is available as a jar file, and had been added to the pom.xml:
<dependency>
<groupId>com.sap.db.jdbc</groupId>
<artifactId>ngdbc</artifactId>
<version>1.96.0</version>
</dependency>
My application properties was as follows:
spring.jpa.hibernate.ddl-auto=create
spring.datasource.url=jdbc:sap://<YOUR SAP HANA IP>:host
spring.datasource.username=sap_hana_user
spring.datasource.password=sap_hana_password
spring.datasource.driver-class-name=com.sap.db.jdbc.Driver
However, that didn't work, and I was still getting the error message:
Cannot determine embedded database driver class for database type NONE.
What did work, was adding the Datasource programmatically, as mentioned here, and removing the application.resources file. So, finally, my Application.java looks like:
#SpringBootApplication
public class Application extends SpringBootServletInitializer
{
#Bean
#Primary
public DataSource dataSource()
{
return DataSourceBuilder.create().username("user_name_sap_hana").password("password_sap_hana").url("jdbc:sap://<YOUR SAP HANA IP>:port").driverClassName("com.sap.db.jdbc.Driver").build();//https://stackoverflow.com/a/28822145/1243462 ; https://stackoverflow.com/a/1336965/1243462
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(Application.class);
}
public static void main(String[] args)
{
SpringApplication.run(Application.class, args);
}
}
Could someone please explain what I did wrong? The first method did not mention having to do anything besides enter these details in application.resources.

Spring data multiple datasources from multiple files

I have 2 (or more) different configuration properties file located in the project and I want to load them for different datasources.
I tried to do as following:
#Bean
#ConfigurationProperties(locations = {"#{myconfigroot.getRootFolder()}/datasource1.properties"}
public static DataSource getFirstDatasource() {
return DataSourceBuilder.create().build();
}
But obviously this won't work as the ConfigurationProperties annotation locations property doesn't go through the spEL. (Or may be I write it wrong?) myconfigroot.getRootFolder() is a static method which returns the path to the datasource1.properties.
Please advice. Thanks.
===== Edited =======
I believe this is a common problem when somebody want their application want to load different configuration files. Due to some reasons the file location and name can't be put in the startup script or command line, or, the path can only be determined in runtime, that would require spring to load them during the bean creation.
I tried once using PropertySourcePlaceHolderConfigurer but seems not work either.
Anybody can share some lights?
Latest Spring boot (version 1.3.5) doesn’t support SpEL in this case.
See JavaDoc of annotation #ConfigurationProperties
Note that contrary to {#code #Value}, SpEL expressions are not
evaluated since property values are externalized.
I found a way to customize Spring boot default behavior as follows:
For example, I have database.properties file in somewhere, for some reason I cannot get the location before runtime.
username=mike
password=password
Accordingly, define POJO mapping to properties:
#Component
#ConfigurationProperties(locations = "myConfiguration")// myConfiguration is customized placeholder
public class MyProperties{
String username;
String password;
//Getters, Setters…
}
Then, to extend default StandardEnvironment:
public class MyEnvironment extends StandardEnvironment {
#Override
public String resolvePlaceholders(String location) {
if (location.equals("myConfiguration")) {
//Whatever you can do, SpEL, method call...
//Return database.properties path at runtime in this case
return getRootFolder() + "datasource.properties";
} else {
return super.resolvePlaceholders(text);
}
}
}
Last, apply it in Spring boot main method entry:
#SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
new SpeedRestApplication()
.configure(new SpringApplicationBuilder(SpeedRestApplication.class).environment(new MyEnvironment()))//Replace default StandardEnvironment
.run(args);
}
}
Once Spring boot starts up, the MyProperties bean name and password fields are injected from database.properties. Then you could wire the MyProperties bean to other beans as configuration.
Hope it helps!
I finally got it work by using the following mechanism:
public class DatasourcePostProcessor implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Properties p = new Properties();
p.load(new FileInputStream(new File(getRootFolder() + "/datasource1.properties")));
Map<String, Object> propMap = new HashMap<>();
for (Map.Entry<Object, Object> entry : p.entrySet()) {
propMap.put(entry.getKey().toString(), entry.getValue());
}
MapPropertySource source = new MapPropertySource("datasource1", propMap);
environment.getPropertySources().addLast(source);
}
}
and register the environment post processor into the spring.factories:
org.springframework.boot.env.EnvironmentPostProcessor=com.myorg.test.DatasourcePostProcessor
Anyway, hope this helps people and accept the first anwer as it enlight me. Also post the following references from the google search that found during research:
Where I found how to wire the property source with the environment: https://github.com/spring-projects/spring-boot/issues/4595
Where I found how to load the customized properties file: How to configure a custom source to feed Spring Boot's #ConfigurationProperties

Controller not invoked by Tomcat when application is deployed as war

I am working on a web application using SpringBoot.
The problem I am facing is as follows. The application is running fine from Eclipse, but when I deploy the project as a war in tomcat 7, it's giving me "HTTP Status 404". No exception found in tomcat logs.
Below is my controller:
#RestController
public class TinyUrlController{
#Autowired TinyUrlService tinyUrlService;
#RequestMapping("/create")
public String createUrl(#RequestParam("url") String url,HttpServletRequest request){
TinyUrl tinyUrl = tinyUrlService.createUrl(url);
return tinyUrl.toString();
}
}
Seems your application have no entry point that's why you got nothing. Just create entry point into your application.
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<Application> applicationClass = Application.class;
}
See also Spring Boot deploying guide
I suggest that you try to build your application layout by using
http://start.spring.io/
It will make a SpringBoot application
It will make java packages right too
Just remember to place your controllers under java package "demo".. otherwise they cannot be "auto wired" without more configuration...
I suggest you make a simple controller that just returns "hello" as a starter...

Categories