I have three different .properties files in a Spring Batch project, and I'm trying to set which .properties file should be used as a JobParameter. I'd like to be able to run the job like so:
java CommandLineJobRunner context.xml jobName region=us
The region should specify which .properties file should be used. The problem is getting the context to recognize the JobParameter. I've tried the following to no avail:
<context:property-placeholder location="classpath:batch.#{jobParameters['region']}.properties"/>
And also:
<util:properties id="batchProperties" location="classpath:batch.#{jobParameters['region']}.properties"></util:properties>
I had heard that adding scope="step" could fix similar issues, but I tried adding that to both of the above solutions and still had exceptions.
I think I'm missing a fundamental idea of why I can't get this working, but I'm unable to figure out what that idea is.
If anyone has any suggestions on getting this working and/or explaining why my previous approaches failed, I'd appreciate it.
This is not the right way to proceed (it is impossible do what you are trying to do).
You have to think that jobParameters is available only when a job is running and only for its composing steps marked with scope="step" (and not <context:property-placeholder> nor <util:properties> has a step attribute).
A way for solving the problem is to load properties file in job's execution context before first step is running with a listener:
public class PropertyLoaderJobExecutionListener extends StepExecutionListenerSupport {
Properties countryProperties;
public void setCountryProperties(Properties pfb) {
this.countryProperties = pfb;
}
#Override
public void beforeStep(StepExecution stepExecution) {
super.beforeStep(stepExecution);
// Store property file content in jobExecutionContext with name "batchProperties"
stepExecution.getJobExecution().getExecutionContext().put("batchProperties", countryProperties);
}
}
in your job.xml
<bean id="loadPropertiesListener" class="PropertyLoaderJobExecutionListener" scope="step">
<property name="pfb">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:batch.#{jobParameters['region']}.properties" />
</bean>
</property>
</bean>
and register this listener in your first step (you can't do that in your JobExectionListsner.beforeJob() because there isn't a scope="job" for now and late-binding of #{jobParameters['region']} value is not available).
To access your data with spEL use this syntax:
#{jobExecutionContext.get('batchProperties').getProperty('language')}
or a better syntax to access properties (IDK spEL so good, sorry).
Hope to be clear and can help to solve your problem.
EDIT (full code of my working job.xml):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.2.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-util-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<job id="sourceJob" xmlns="http://www.springframework.org/schema/batch">
<step id="step1">
<tasklet ref="getRemoteFileTasklet" />
<listeners>
<listener ref="loadPropertiesListener" />
</listeners>
</step>
</job>
<bean id="loadPropertiesListener" class="PropertyLoaderJobExecutionListener" scope="step">
<property name="pfb">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:batch.#{jobParameters['region']}.properties" />
</bean>
</property>
</bean>
<bean id="getRemoteFileTasklet" class="GetRemoteFileTasklet" />
Related
I'm trying to migrate from Camel 2.X to 3.X and have run in to a question about logging the routing trace.
Previously I have configured it like this in my application context xml-file:
<bean id="camelTracer" class="org.apache.camel.processor.interceptor.Tracer">
<property name="traceExceptions" value="false" />
<property name="traceInterceptors" value="true" />
<property name="logLevel" value="DEBUG" />
<property name="logName" value="com.mycompany.routing.trace" />
</bean>
<bean id="traceFormatter" class="org.apache.camel.processor.interceptor.DefaultTraceFormatter">
<property name="showBody" value="true" />
<property name="maxChars" value="0" />
</bean>
But that obviously does not work anymore.
From the migration guide on the Camel website:
"A new tracer has been implemented and the old tracer has been removed. The new tracer logs messages at the org.apache.camel.Tracing logger name which is hardcoded. The format of the output is also updated to make it better. The tracer can be customized."
If I set .tracing() at the start of my routes it does log the trace. The name is hardcoded which is fine, but I would like to change the level from INFO to DEBUG among other things.
Does anyone know where to find information on how to configure this "new" tracer (preferrably in an applicationContext.xml file)? Or anywhere else, maybe in the Java DSL route? Or if it is even possible?
Thanks!
Logging level of DefaultTracer cannot be changed by configuration. You need to implement customized Tracer and bind this implementation to registry.
Tracer:
public class TracerCustom extends DefaultTracer {
private static final Logger LOG = LoggerFactory.getLogger("com.stackoverflow.camel.TracerCustom");
#Override
protected void dumpTrace(String out) {
LOG.debug(out);
}
// Customize other methods if needed
}
Spring context:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean class="com.stackoverflow.camel.TracerCustom" />
<camelContext id="tracerCamelContext" xmlns="http://camel.apache.org/schema/spring">
<route trace="true">
<from uri="timer:test"/>
<to uri="log:test"/>
</route>
</camelContext>
</beans>
I'm trying to add a simple redirect into a web application built in Restlets, and it's proving non-trivial. The task is a simple one: I want to actually redirect all missing files from a web application to the same static file.
I'm using org.restlet.routing.Redirector with the following values (I'm using Spring injection):
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<constructor-arg ref="trackerComponentChildContext" />
<property name="attachments">
<map>
<entry key="/api" value-ref="apiRouter" />
<entry key="/statics" value-ref="staticsDirectory" />
<entry key="/" value-ref="staticsRedirector" />
</map>
</property>
</bean>
<bean id="staticsRedirector" class="ca.uhnresearch.pughlab.tracker.restlets.CustomRedirector">
<constructor-arg ref="trackerComponentChildContext" />
<constructor-arg value="{o}/statics/index.html" />
<constructor-arg value="7" />
</bean>
I can play with the file hierarchy relatively simply, but I just want to send anything that doesn't match either /api or /statics to /statics/index.html within the same application.
Restlet is almost getting it, and it does seem now to pick up the reference to the correct file, it just doesn't quite serve it.
I've put a working copy of the whole thing (including Thierry's suggestions below) at: https://github.com/morungos/restlet-spring-static-files. What I'd like to happen is something like the equivalent sequential attempts below:
curl http://localhost:8080/statics/**/* to hit the corresponding /statics/**/*
curl http://localhost:8080 to hit the main /statics/index.html
curl http://localhost:8080/**/* to hit the main /statics/index.html
I made some tests regarding your issue and I can't figure out how to have your message :-(. Perhaps it's because I haven't the whole code.
In fact, I saw a problem at the level of the SpringRouter itself. I would like to attach the redirector with an attachDefault and not an attach("/", ...) / attach("", ...). The method setDefaultAttachment actually does an attach("", ...).
So I made work something with the following updates:
Create a custom SpringRouter
public class CustomSpringRouter extends SpringRouter {
public void setDefaultAttachment(Object route) {
if (route instanceof Redirector) {
this.attachDefault((Restlet) route);
} else {
super.setDefaultAttachment(route);
}
}
}
Create a custom Redirector. I got the context from the component instead of a child context.
public class CustomRedirector extends Redirector {
public CustomRedirector(Component component, String targetPattern, int mode) {
super(component.getContext(), targetPattern, mode);
}
}
I then use the following Spring configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="router" />
</bean>
<bean name="router" class="test.CustomSpringRouter">
<property name="attachments">
<map>
<entry key="/api" value-ref="apiRouter" />
<entry key="/statics" value-ref="staticsDirectory" />
</map>
</property>
<property name="defaultAttachment" ref="staticsRedirector" />
</bean>
<bean id="staticsRedirector" class="test.CustomRedirector">
<constructor-arg ref="myComponent" />
<constructor-arg value="{o}/statics/index.html" />
<constructor-arg value="7" />
</bean>
<bean name="apiRouter" class="org.restlet.ext.spring.SpringRouter">
(...)
</bean>
(...)
</beans>
Hope it helps you,
Thierry
I am using spring batch in my application. I am using multithreading using spring batch at step level .
Below is my configuration:
<step id="testStep">
<tasklet
task-executor="taskExecutor" throttle-limit="5">
<chunk reader="reader" writer="writer"
commit-interval="10">
</chunk>
</tasklet>
</step>
<bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter"
scope="step">
<property name="resource" value="file:${output.file.location.directory}/<<<NEED TO PASS CURRENT EXECUTING THREAD NAME>>>" />
<property name="appendAllowed" value="true"></property>
<property name="lineAggregator" ref="aggregator" />
</bean>
So in resource property of flatfileitemWriter , i need to pass current executing thread name, so that each thread wil write in a separate unique file.
I can able to get the current executing thread name in writer class but dont know how to get the same in the spring confirguration file .
Any idea to do so ?
If you want you can use spEL; with this scripting language you can use standard java method and to retrive current thread name the syntax is #{T(java.lang.Thread).getCurrentThread().getName()}.
In a more clear way use a StepExecutionListener.beforeStep() injecting the current thread name into step execution context (eg. currentThreadName key) and retrieve from step xml config in the standard way
<property name="resource" value="file:${output.file.location.directory}/#{stepExecutionContext['currentThreadName']}" />
I have a spring bean (let's call it MagicBean) which has a HashSet<String> as one of its properties.
I know how to init a set like this:
<bean id="mySet" class="org.springframework.beans.factory.config.SetFactoryBean">
<property name="targetSetClass" value="java.util.HashSet"/>
<property name="sourceSet">
<set>
<value>Value 1</value>
<value>Value 2</value>
<value>Value 3</value>
</set>
</property>
</bean>
<bean id="magicBean" class="MagicBean">
<property name="mySet" ref="mySet"/>
</bean>
Is there a way to set the values in the set using values from .properties file instead of hard-coding those value in the xml?
Update 1:
Since I might have different numbers of values for this set in different environments, using the hard-coded set in the xml won't work. That's why I need to somehow fetch these values from a properties file.
Update 2:
I came up with a quick-and-dirty way to do this which is list all values as ONE single string in the .properties file and then set this value to the MagicBean. Then in Java code, parse this string.
Any better idea?
You can use:
<value>${my.set.value1}/value>
And set the value in property file:
my.set.value1=Value1
try something like this
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
">
<bean class="B1">
<property name="Props">
<util:properties location="classpath:test.properties" />
</property>
</bean>
</beans>
this is B1
class B1 {
public void setProps(Properties props) {
...
}
}
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="meassageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="resource\message">
</property>
</bean>
</beans>
Main.java class file
public class Main {
public static void main(String[] args) {
ApplicationContext context= new ClassPathXmlApplicationContext("spring.xml");
System.out.println(context.getMessage("emp", null, Locale.US));
}
}
My properties file is in src/resource folder. File name is mesaage_en_US.properties.
I have also tried with different file names like message.property, message_en.property and with different locales like Locale.English, Locale.UK but no luck.
I moved the property file to src folder but getting same exception.
I am getting following exception.
Exception in thread "main" org.springframework.context.NoSuchMessageException: No message found under code 'emp' for locale 'en_US'.
at org.springframework.context.support.DelegatingMessageSource.getMessage(DelegatingMessageSource.java:65)
at org.springframework.context.support.AbstractApplicationContext.getMessage(AbstractApplicationContext.java:1234)
at org.beans.Main.main(Main.java:14)
Please help.
message_en_US.properties
emp=Hello Employee.
I like to use a PropertySourcesPlaceholderConfigurer for that. Here's a great tutorial to get you started.
Basically, you'll want to add:
<context:property-placeholder location="classpath:foo.properties" />
to your spring xml config file, where "foo.properties" is a resource's absolute path within the class path.
Then you can inject them into fields like this:
#Value( "${jdbc.url}" )
private String jdbcUrl;
where "jdbc.url" is the reference name in your properties file.
Of course, the #Value won't work inside your static void main, but I really doubt static void main is where you want to use your properties anyway. You ought to be accessing them from a Spring Bean.
I think this is duplicated from this question. Basically, it has to do with a mismatch between your bundle and the locale specified in code.
Instead of getting message from ApllicationContext I am getting message from MeassageSource itself. I changed my spring.xml like this
<bean id="employee" class="org.bean.Employee" >
<property name="id" value="1"/>
<property name="name" value=""/>
<property name="dept" value=""/>
<property name="messages" ref="messageSource"/>
</bean>
Now I am calling messages.getMessage(this.messages.getMessage("emp", null, Locale.UK)) from Employee class. Its working.
Change to
<property name="basename" value="message" />
with message_en_US.properties in the same folder as your spring.xml.
EDIT : You have a typo in your bean name when defining the MessageSource. It's name should have been exactly messageSource. Because of that extra a in meassageSource ApplicationContext failed to load it.
I've reproduced your error and found the problem. It has to do with Spring not finding the bundle. I think you should be getting a warning before the exception with the following message:
WARNING: ResourceBundle [resource\message] not found for MessageSource: Can't find bundle for base name resource\message, locale en_US
This has been the hint. The problem is related to your project structure and how the bundles are searched when specifying setBasename property. Please take a look at this.
Anyway I think you should put your bundles in the more standard location src/main/resources. If you follow this convention, your messageSource bean should be defined like this:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="message" />
</bean>
With this approach your example should produce the desired line:
Hello Employee.