jar with embedded tomcat, when using another spring project, is not working with just yml - it's needing a blank application.properties file as well - spring-boot

Been searching for others that have run into this issue, and not finding much out there, so it can't be that common.
I have a spring-boot project that I want to convert into a jar project, running with embedded tomcat. It's using yml files (application.yml and then the profile versions - eg appplication-dev.yml.) It ran fine as war with the yml files, however, when I convert it to a jar, and kick off the jar, the embedded tomcat never starts UNLESSS I add an empty application.properties file as well. (No errors just no Tomcat startup unless the empty application.properties file is added.)
I believe it's somehow related to one of our internal jar dependencies (also spring), since if I remove that dependency from the pom (and any of the code referencing it) I can get the jar to startup the embedded tomcat just fine (without providing the empty application.properties file.)
I could also, of course, forgo using yml files and just use .properties files, but I'd like to use yml files if possible. Why adding an empty applcation.properties file causes things to work has me stumped.
If it helps, the config in the dependency project that causes the issue we're seeing is set up as:
#Configuration
#EnableConfigurationProperties(OracleDataSourceProperties.class)
#EnableTransactionManagement
#ComponentScan(basePackages = {"com.foo.data.services","com.foo.data.domain", "com.foo.utility", "com.foo.cipher.utility"})
#MapperScan(value = {"com.foo.data.services.mapper","com.foo.data.services.batchmapper"})
public class DataServicesPersistenceConfig { ... }
and the OracleDataSourceProperties class:
#ConfigurationProperties(prefix="oradb", ignoreUnknownFields = true)
public class OracleDataSourceProperties extends BaseVO implements InitializingBean{

Related

Spring Boot Application - Running jar file gives ResourceFinderException error

Created a jar file for a spring boot multimodule application and ran the jar file using java -jar command. While starting the application, it gives ResourceFinderException. When I analyzed it, the issue is happening because in my ResourceConfig file, i have used the package for my api end points. If I use register(service.class), the application starts fine. Any suggestion how can I provide the package instead of using register? The reason I want to use package is because I have lots of services inside multiple packages and the code looks very ugly if i use register for all the services. The ResourceConfig file looks like below.
public class AppResourceConfig extends ResourceConfig {
public AppResourceConfig {}{
super();
property("jersery.config.beanValidation.enableOutputValidationErrorEntity.server");
**packages("com.api");**
register(GsonProvider.class);
register(RequestContextFilter.class);
register(NotFoundExceptionMapper.class);
register(DefaultExceptionMapper.class);
}
}
Here the issue is with highlighted line: packages("com.api")
If I comment out this code application will be up. Otherwise it is giving org.glassfish.jersey.server.internal.scanning.ResourceFinderException: java.io.FileNotFoundException: api-01.03.00.04-snapshot.jar (No such file or direcotry)
Note: api-01.03.00.04-snapshot.jar is the jar file for one of the module in a project

Spring boot: populate h2 db from schema in test/resources

On my local machine I load an in-memory h2 database to start my spring boot application in a safe environment, here's the properties:
spring.datasource.url: jdbc:h2:mem:DB_TEST;Mode=Oracle
spring.datasource.platform: h2
spring.jpa.hibernate.ddl-auto: none
spring.datasource.continue-on-error: false
spring.jpa.database-platform: org.hibernate.dialect.Oracle10gDialect
Then, in my src/main/resources I have the file schema-h2.sql containing my local db initiations.
That's fine, but then I also have some junit tests I want to execute:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTest {
#Autowired
private MyController controller;
#Test
public void myTest(){
controller.doSomething();
}
This is also fine, as the schema-h2.sql is seen.
Anyway according to me it would be better to put the schema-h2.sql in src/test/resources as it has to be used only on my local environment. Doing so also allows maven to exclude it from the final build and that is also pretty fine.
Anyway if I put it there the test keeps working...but the main application breaks as the schema-h2.sql is not found!
How to modify the above properties to specify that the shema-h2.sql has to be searched inside of the test/resources folder?
Thanks
For normal mode, the properties file is put in src/main/resources,
and for the testing method, the properties file in the src/test/resources folder.
By Trying to run a test-class, eclipse runs EACH file ending with .sql (and thus containing a script to create tables or to insert data) it finds under src/main/resources and src/test/resources.
So if you put a script-file schema.sql (containing a script that creates a table: create table ..) in both folders, you'll get an "table already exits" error, if you let jut one, the test will run smoothly.
If you put a script-file (that insert data in a table) in both folder, both scripts will be run.
You can also use the #PropertySource("..") in your repository to tell spring where to find the properties-file to use.

Spring boot on Tomcat with external configuration

I can't find an answer to this question on stackoverflow hence im asking here so I could get some ideas.
I have a Spring Boot application that I have deployed as a war package on Tomcat 8. I followed this guide Create a deployable war file which seems to work just fine.
However the issue I am currently having is being able to externalize the configuration so I can manage the configuration as puppet templates.
In the project what I have is,
src/main/resources
-- config/application.yml
-- config/application.dev.yml
-- config/application.prod.yml
-- logback-spring.yml
So how can I possibly load config/application.dev.yml and config/application.prod.yml externally and still keep config/application.yml ? (contains default properties including spring.application.name)
I have read that the configuration is load in this order,
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
Hence I tried to load the configuration files from /opt/apache-tomcat/lib to no avail.
What worked so far
Loading via export CATALINA_OPTS="-Dspring.config.location=/opt/apache-tomcat/lib/application.dev.yml"
however what I would like to know is,
Find out why loading via /opt/apache-tomcat/lib classpath doesn't work.
And is there a better method to achieve this ?
You are correct about load order. According to Spring boot documentation
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:
A /config subdirectory of the current directory.
The current directory
A classpath /config package
The classpath root
The list is ordered by precedence (properties defined in locations higher in the list override those defined in lower locations).
[Note]
You can also use YAML ('.yml') files as an alternative to '.properties'.
This means that if you place your application.yml file to /opt/apache-tomcat/lib or /opt/apache-tomcat/lib/config it will get loaded.
Find out why loading via /opt/apache-tomcat/lib classpath doesn't work.
However, if you place application.dev.yml to that path, it will not be loaded because application.dev.yml is not filename Spring is looking for. If you want Spring to read that file as well, you need to give it as option
--spring.config.name=application.dev or -Dspring.config.name=application.dev.
But I do not suggest this method.
And is there a better method to achieve this ?
Yes. Use Spring profile-specific properties. You can rename your files from application.dev.yml to application-dev.yml, and give -Dspring.profiles.active=dev option. Spring will read both application-dev.yml and application.yml files, and profile specific configuration will overwrite default configuration.
I would suggest adding -Dspring.profiles.active=dev (or prod) to CATALINA_OPTS on each corresponding server/tomcat instance.
I have finally simplified solution for reading custom properties from external location i.e outside of the spring boot project. Please refer to below steps.
Note: This Solution created and executed windows.Few commands and folders naming convention may vary if you are deploying application on other operating system like Linux..etc.
1. Create a folder in suitable drive.
eg: D:/boot-ext-config
2. Create a .properties file in above created folder with relevant property key/values and name it as you wish.I created dev.properties for testing purpose.
eg :D:/boot-ext-config/dev.properties
sample values:
dev.hostname=www.example.com
3. Create a java class in your application as below
------------------------------------------------------
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
#PropertySource("classpath:dev.properties")
#ConfigurationProperties("dev")
public class ConfigProperties {
private String hostname;
//setters and getters
}
--------------------------------------------
4. Add #EnableConfigurationProperties(ConfigProperties.class) to SpringBootApplication as below
--------------------------------------------
#SpringBootApplication
#EnableConfigurationProperties(ConfigProperties.class)
public class RestClientApplication {
public static void main(String[] args) {
SpringApplication.run(RestClientApplication.class, args);
}
}
---------------------------------------------------------
5. In Controller classes we can inject the instance using #Autowired and fetch properties
#Autowired
private ConfigProperties configProperties;
and access properties using getter method
System.out.println("**********hostName******+configProperties.getHostName());
Build your spring boot maven project and run the below command to start application.
-> set SPRING_CONFIG_LOCATION=<path to your properties file>
->java -jar app-name.jar

Loading a file as a string content in a Spring service contained in a JAR built with Maven

I am using Spring with Maven and I would like to load a file contained in the maven folder src/main/resources inside a spring service annotated with #Service contained in a JAR file (used by another WAR file as a Maven dependency). This does not seem to work for many different reasons (read: for many different exceptions according to many different attempts).
I know that Maven, when building, puts the stuff in src/main/resources at the root of the JAR file. I've opened the JAR file and I've verified that.
I would prefer a File object (something I can use with utility libraries like Apache Commons IO), but also "something" else that's working is fine.
I would prefere going for stuff within the Java class annotated with #Service (i.e. no application context XML file, with stuff inside that I recall within the service etc.)
The file contains stuff that I eventually use as a List<String> where every item is a line in the original file. I need to keep this file as it is, I can not use a database or another solution different than a file.
This is not a .properties file and is not intended to be.
The following are the not working attempts (throwing NullPointerException, FileNotFoundException, IllegalArgumentException: URI is not hierarchical and so on...).
Among the attempts I have tried also adding/removing the prefix src/main/resources while trying to figure out what was wrong, but then I figured out that Maven puts all the stuff at the root of the JAR file as stated before.
#Value("classpath:src/main/resources/myFile.txt")
private Resource myFileResource;
private File myFile =
new File(this.getClass().getResource("/myFile.txt").toURI());
private File myFile =
new FileSystemResource("src/main/resources/myFile.txt").getFile();
private BufferedReader bufferedReaderMyFile =
new BufferedReader(
new InputStreamReader(getClass().getResourceAsStream("myFile.txt")));
What's wrong with what I am doing?
File inside jar file is no longer a File, so treat it as Resource and read it via Stream
InputStream inputStream = this.getClass().getResourceAsStream("/myFile.txt");
assumming you have actually packed that myFile.txt inside jar file and it is available at the root of JAR file

maven spring test application contexts

I want to use the applicationContext.xml in my src/main/resources directory from within my test harness in src/test/java. How do I load it? I have tried:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="classpath:applicationContext.xml")
public class TestService {
...
}
but get a file not found error. I'm using Maven and Spring. Thanks.
Try this (note the asterisk):
#ContextConfiguration("classpath*:applicationContext.xml")
The Maven test classpath uses the files in target/test-classes. That folder contains Java classes from src/test/java and resources from src/test/resources.
The way to go is to create a test specific app context and store it under src/main/resources.
You may try to reference the file directly using file: i.e. something like file:src/main/resources/applicationContext.xml but to me this is an ugly hack.
Also, you can of course use the Maven resources plugin to copy applicationContext.xml prior to test execution.
Here's how I do it, it may or may not be the best way for you. The main thing is it works in both Eclipse and Maven:
Keep exactly one copy of each applicationContext-xxx.xml file per project. NEVER copy-and-paste them or their contents, it'll create a maintenance nightmare.
Externalize all environmental settings to properties files (e.g. database-prod.properties and database-test.properties) and place them in src/main/resources and src/test/resources respectively. Add this line to your app contexts:
<context:property-placeholder location="classpath:**/*.properties"/>
Create a superclass for all test classes and annotate it with a context configuration:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:applicationContext.xml"})
#Ignore
public class SpringEnabledTest {
// Inheritable logger
protected Logger logger = LoggerFactory.getLogger(this.getClass());
}
Add <additionalClasspathElements> to your maven-surefire-plugin configuration to make sure surefire picks up appContext and the right properties files. I've created an example here.
Add the location(s) of the app context files and src/test/resources to your Eclipse classpath so you can execute unit tests in Eclipse as well.
NEVER add src/main/resources to your Eclipse classpath, it's only a convenient place for Maven to package additional source files, it should have no bearing on Eclipse. I often leave this directory blank and create additional folders (e.g. env/DEV, env/UAT and env/PROD) outside of the src/ folder and pass a parameter to the build server and let it know from which folder it needs to copy files to src/main/resources.
Add the src folder to the classpath of your testing tool. If it's in Eclipse, I think you can do it from the project properties. You may have to change it to classpath:**/applicationContext.xml as well.

Resources