Spring Batch CommandLineJobRunner can't find .xml configuration file - java

I'm a total beginner in Spring Batch Framework, and I found easily understandable codes from http://www.javabeat.net/introduction-to-spring-batch/ to use as a learning tools. I have my project set up in Eclipse similiar to the codes from the page, it looks like this :
and the code executes the jobs in fileWritingJob.xml using CommandLineJobRunner like so :
package net.javabeat.articles.spring.batch.examples.filewriter;
import org.springframework.batch.core.launch.support.CommandLineJobRunner;
public class Main {
public static void main(String[] args) throws Exception {
CommandLineJobRunner.main(new String[]{"fileWritingJob.xml", "LayeredMultiThreadJobTest"});
}
}
and it runs as expected with no problem. But when I move the fileWritingJob.xml to another dir (still under the project dir) it doesn't run. I've tried changed the filename arguments at the CommandLineJobRunner method using relative and full path but it still doesn't run. For example, if create a directory under the project directory (same level as config) named jobs and put the xml there then pass the filepath to CommandLineJobRunner like this:
CommandLineJobRunner.main(new String[]{"/jobs/fileWritingJob.xml", "LayeredMultiThreadJobTest"});
or this
CommandLineJobRunner.main(new String[]{"../jobs/fileWritingJob.xml", "LayeredMultiThreadJobTest"});
it doesn't work.
But when I tried creating a subdir under the config directory and put the fileWritingJob.xml there, like this
CommandLineJobRunner.main(new String[]{"configsubdir/fileWritingJob.xml", "LayeredMultiThreadJobTest"});
it runs. It's as if the CommandLineJobRunner only checks the config directory.
I'm running out of ideas, can anyone help me?
UPDATE : After digging around a bit, thanks to Michael Minella's suggestion about ClassPathXmlApplicationContext I am able to put the xml wherever I want. I also consulted to this page Spring cannot find bean xml configuration file when it does exist and http://www.mkyong.com/spring-batch/spring-batch-hello-world-example/
So what I do now is declaring a new context by using ClassPathXmlApplicationContextand then run it using job launcher, here is how :
public static void main(String[] args) {
String[] springConfig =
{
"file:/path/to/xml/file"
};
ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean("JobName");
try {
JobExecution execution = jobLauncher.run(job, new JobParameters());
System.out.println("Exit Status : " + execution.getStatus());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done");
}
Thank you very much for all your inputs!

A little detail around what is happening when you pass in the path to the xml based job definition to the CommandLineJobRunner. All we do is we pass that string to the constructor of ClassPathXmlApplicationContext. Because of that, it is expected that the xml file for the job definition be on the application's classpath. I can't tell from your project screen shot how you are building the project so I'm not sure if the config directory is on your classpath or not. However, if it is on the classpath and lives at the root of it, I'd expect you to be able to pass the path to the fileWritingJob.xml as "/config/fileWritingJob.xml".
The source for this class can be helpful when debugging this type of issue. You can find the source code for the CommandLineJobRunner here: https://github.com/spring-projects/spring-batch/blob/master/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java

You can specify the job path relative to your config directory. For example, if fileWritingJob.xml is in config/jobs/ directory, then in you can execute the job as follows:
CommandLineJobRunner.main(new String[]{"jobs/fileWritingJob.xml", "LayeredMultiThreadJobTest"});
Likewise, if the job configuration file is outside of the config directory, you can write:
CommandLineJobRunner.main(new String[]{"../fileWritingJob.xml", "LayeredMultiThreadJobTest"});
You can specify an absolute path for locating the job.

Related

Is there a way to get list of loaded properties file in SpringBoot application?

I'm facing an issue on only one platform when I'm trying to execute mvn clean install. As part of the build we compile multiple component and last we execute functional testing using wiremock. It is supposed to pick specific configuration from function testing profile and default properties should be picked from application.properties file. But for some reason same code isn't able to find the properties mentioned in these file. So, just wondering if somehow, if I can get the list of properties files being loaded during wiremock ? This will give some clue on why isn't expected properties files are being picked ?
All properties files are located inside :
src/main/resources
And, following from test class.
#ContextConfiguration(classes = SampleFTConfiguration.class)
public class SampleControllerTest{
//test method
}
#ComponentScan("com.xxx.xxx.xxx.ft")
#PropertySource("classpath:application-test.properties")
public class SampleFTConfiguration{
}
Note : I'm not expecting anyone to fix the issue, all I wanted to know, if we can get the name of loaded property files ?
After searching and trying out for a while, looks like ConfigurableEnvironment is what you're trying to find.
The code is pretty simple. However I think it's better to debug and check the configurableEnvironment value directly, so you can adjust the code to your needs (remove filter name, etc).
#Autowired
private ConfigurableEnvironment configurableEnvironment;
#Test
public void getProperties() {
Map<String, Object> mapOfProperties = configurableEnvironment.getPropertySources()
.stream()
.filter(propertySource -> propertySource.getName()
.contains("application-test.properties"))
.collect(Collectors.toMap(PropertySource::getName, PropertySource::getSource));
mapOfProperties.values()
.forEach(System.out::println);
}
the code will printed out
{properties-one=value-for-properties-one,
properties-two=value-for-properties-two}
with my application-test.properties value
properties-one=value-for-properties-one
properties-two=value-for-properties-two
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/env/ConfigurableEnvironment.html
Ok, following the test definition please make sure that:
You should run the test with spring runner (spring extension if you're on JUnit5). So you should place the annotation #RunWith(SpringRunner.class) (or #ExtendsWith(SpringExtension.class) for junit 5)
The property source you're using is application-test.properties. You've said that the properties file is located in src/main/resources but the file name probably implies that it should reside in src/test/resources
To troubleshoot context configuration on app startup (for those who don't have an access to app sources) you can add
logging:
level:
org.springframework.boot.context.config: trace
to your application.yml to get filenames:
2022-10-26 13:29:45.977 TRACE [,,] 16522 --- [main] o.s.b.context.config.ConfigDataLoaders : Loading file [config/application-app.
yml] using loader org.springframework.boot.context.config.StandardConfigDataLoader
2022-10-26 13:29:45.977 TRACE [,,] 16522 --- [main] o.s.b.context.config.ConfigDataLoaders : Loading file [config/application-ppe.
yml] using loader org.springframework.boot.context.config.StandardConfigDataLoader
Adding org.springframework.boot.context.properties: trace doesn't help much though. Some user-defined properties get logged, however others don't.

How to get the classpath resource basename of a config file in Spring proejct?

In my Spring proejct, I created a config.conf to store some variables.
And in a class I need to read these variables.
I am using the config library, https://github.com/lightbend/config.
And as the document says, I tried to used
private static final Config config = ConfigFactory.load(basename here goes here);
to load that config.conf file.
And by the document, https://lightbend.github.io/config/latest/api/com/typesafe/config/ConfigFactory.html#load-java.lang.String-, this method receives a string as classpath resource basename.
I am wondering how to get this "classpath resource basename" of the config.conf file?
My project's structure:
Thanks!
you can access file like this.
URL url = EmailService.class.getClassLoader().getResource("config.conf");
to load config, you can access path like below
config.load(url.getPath());

Splitting resources.groovy (Grails 2.5) to make it modular

resources.groovy of my Grails project is growing, so I am thinking of splitting it in some logical files that will be easier to mantain. After reading some questions, blogs, i got to know about importBeans or loadBeans. This works well only when application is run using grails run-app. However we I create a war and deploy it on JBoss, the custom bean files are not accessible.
Also tried below with no luck - mentioned here -
Load spring beans from custom groovy files in grails app
grails.war.resources = { stagingDir, args ->
copy(todir: "${stagingDir}/WEB-INF/classes/spring") {
fileset(dir:"grails-app/conf/spring") {
include(name: "datasourceDefinitions.groovy")
exclude(name: "resources.groovy")
}
}
}
I have added datasourceDefinitions.groovy in grails-app/conf/spring.
Please help.
The problem is due to the Spring bean configuration files are moved into the folder "WEB-INF/classes/spring/"(make sure the file is packaged in the.war) inside the WAR file. As what I did was locate the resource path in resources.groovy.
def loadFromFile = { name ->
importBeans("file:grails-app/conf/spring/"+name)
}
def loadFromWar = { name ->
def resource = application.parentContext.getResource("WEB-INF/classes/spring/"+name)
loadBeans(resource)
}
def loadResource = application.isWarDeployed() ? loadFromWar : loadFromFile
loadResource "datasourceDefinitions.groovy"
loadResource "anotherBean.groovy"

How can I confirm that ClassPathXmlApplicationContext finds a file?

We have an app that does not run in a web container so I am trying to start Spring up. In the "main" function we have this:
public static void main(String[] args) throws InterruptedException, IOException, AlreadyAliveException, InvalidTopologyException, AuthorizationException {
// starting up spring...
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:/applicationContext.xml");
DataSourceTransactionManager dstm = applicationContext.getBean("markiscool", DataSourceTransactionManager.class);
dstm.toString();
}
And my applicationContext.xml contains:
<bean id="markiscool" class="blah.entities.LocationEntity" />
The app, on startup, logs this:
[INFO] ClassPathXmlApplicationContext - Refreshing
org.springframework.context.support.ClassPathXmlApplicationContext#295cd6e5:
startup date
[Thu Dec 17 10:28:28 CST 2015]; root of context hierarchy
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'markiscool' is defined
I have tried putting garbage in the xml file but it doesn't fail so it must not be finding the file. Also, the file is on the classpath:
I have also stuck the file in about every place I can think of. It does not load. Help!
Try to use FileSystemXmlApplicationContext:
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("/conf/applicationContext.xml");
Instead of:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:/applicationContext.xml");
Try this:
Right click -> properties -> Deployment Assembly
Put an entry: conf -> /
try
new ClassPathXmlApplicationContext("applicationContext.xml");
1) Put applicationContext.xml into the config folder
2) Tell Maven to include applicationContext.xml in the jar it creates
3) Use the function ClassPathXmlApplicationContext to pull in the file
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:conf/applicationContext.xml");
Note: FileSystemXmlApplicationContext will not work if you are trying to access a file inside of a jar. For this reason I recommend using ClassPathXmlApplicationContext because it works whether you are running the project in eclipse or via the jar

Changing the log path on fly

I am using log4j for logging activities in my application.I want to take the log path from data base.
now I need to configure my log4j properties dynamically.
can we do it on the fly we change the log4h logging path..
Please suggest.
Thanks
You should create a class what loads at the startup and configurate the log4j.
Here is a code what I used in a JavaEE project, what loads the configuration file from an outer directory:
public class InitListener implements ServletContextListener {
public InitListener() {
}
public void contextInitialized(ServletContextEvent sce) {
try {
File file = null;
file = new File(System.getProperty("catalina.base") + "/conf/query-log4j.xml");
DOMConfigurator.configure(file.toURL());
System.out.println("Log4J successfully configured!");
} catch(Exception e) {
System.out.println("There was an error when initialize the Log4J config!");
e.printStackTrace();
throw new RuntimeException(e);
}
}
public void contextDestroyed(ServletContextEvent sce) {
}
}
If you were using MentaLog, all you have to do was that:
yourLogger.setFilename("newfilenamehere.log");
Your log would be automatically re-opened with the new name. In my personal opinion, programmatic configuration is the way to go over XML and/or annotations. It provides unmatched flexibility and easy of use.
Create seperate properties file to hold Specific Enviroment related settings example:
**uatLog4j.properties**
#######################
UAT Settings
#######################
{Add your Settings here}
And another for sy production enviroment.
**productionLog4j.properties**
########################
PRODUCTION settings
########################
{Add your Settings here}
And then using say the IP Address or the Server Name to determine the deployed platform, pass the path from the database to the required enviroment properties file as required.
Alternatively you can use LogManager to retreive Logger instances or to operate on the current LoggerRepository. See Javadoc and a RepositorySelecter example.
NOTE:
You can achieve the same using XML.

Categories