Below is the code for my camel exec component. This is one of my first times using Camel and I'm just trying to get camel exec to execute a batch file for me. Can anyone tell me how I misconfigured the workingDir parameter? As part of the error I am getting that 1 parameters couldn't be set. If it matters I'm working on windows.
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
/**
* A Camel Java DSL Router
*/
public class MyClass {
public static void main(String args[]) throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() {
from("direct:exec").to("exec:mybat.bat?workingDir=C:/Users/userName/Desktop");
}
});
context.start();
Thread.sleep(10000);
context.stop();
}
}
EgoKilla, below is the working code
public class MyClass {
public static void main(String args[]) throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() {
from("timer:foo?period=5000").to("exec:D:/jboss/test.bat?");
}
});
context.start();
Thread.sleep(10000);
context.stop();
}
}
Here for every 5000 msec, camel executes the batch file. I give the fully qualified path of the bat file.
Working directory means, the directory which the command should be executed. ex: if you trying to create a file using batch file, that file will be created in the working directory specified.
Hope it helps!!
Related
I have written a script in while I have a schedular class that does something in after every certain time interval and another class in which I am watching a folder continuously for the occurance of any new file. And these both jobs (Schedular + WatchService) has to be endless.
But they are not getting called concurrently.
Called schedular class by - #Scheduled & #ComponentScan(basePackages = "com.project.schedular")
Calling WatchService by - #PostConstruct on method
Already tried Putting #PostConstruct on both and putting both packages in #ComponentScan({"com.project.schedular","com.project.watcher"})
Also tried putting #Async on both the methods.
Main Class:
#SpringBootApplication
#EnableScheduling
#ComponentScan(basePackages = "com.aprstc.schedular")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Component
public class SchedularClass {
#PostConstruct
#Scheduled(fixedRate = 30000)
public void execute() {
//logic of scheduling method
}
Watcher Class:
#Component
public class WaybillReadScript {
#PostConstruct
public void watchFolder() throws InterruptedException, IOException {
System.out.println("Into the watch Folder.");
WatchService watchService = FileSystems.getDefault().newWatchService();
System.out.println(2);
Path path = Paths.get("/home/mypc-630/Work/abc");
System.out.println(3);
try {
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
} catch (IOException e) {
e.printStackTrace();
}
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
if (event.context().toString().equalsIgnoreCase("wbill.txt"))
processWaybillFile();
}
key.reset();
}
}
}
I expect that both classes must run concurrently.
Watcher Must do continuous watching.
And the scheduler must do a continuous scheduled job.
I think the PostConstruct is the wrong place. PostConstruct is used to initialize your beans/components. And if you make a blocking call with watchService.take(), this PostContruct will never be left and if not all beans are completely created than your application with the scheduler will not start.
Can something like this be accomplished using Spring Boot?
The idea is to group properties and assign the same value to all of them, so instead of all of the properties ending with 'test*' i would like to change just one property 'my.flag'. I know that such functionality works in case of loggers, but can I define my own group?
I am not sure whether your problem has been solved or not, but I want to provide a solution to achieve what you want by using spring.factories and implementing ApplicationListener as following steps.
STEP 1
Create a class MyPropertiesListener which implements ApplicationListener and read the value of my.flag in application.properties first, then set it to all the properties whose key starts with my.flag..
public class MyPropertiesListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
#Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment env = event.getEnvironment();
String myFlag = env.getProperty("my.flag");
Properties props = new Properties();
MutablePropertySources propSrcs = env.getPropertySources();
StreamSupport.stream(propSrcs.spliterator(), false)
.filter(ps -> ps instanceof EnumerablePropertySource)
.map(ps -> ((MapPropertySource) ps).getPropertyNames())
.flatMap(Arrays::<String>stream)
.forEach(propName -> {
if (propName.toString().startsWith("my.flag.")) {
props.put(propName, myFlag);
}
});
env.getPropertySources().addFirst(new PropertiesPropertySource("myProps", props));
}
}
STEP 2
Create a file named spring.factories under src/main/resource/META-INF and configure MyPropertiesListener into it.
org.springframework.context.ApplicationListener=xxx.xxx.xxx.MyPropertiesListener
TEST
The value of my.flag.test3 is false in application.properties originally, but it is going to be overwritten as true while application starts.
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Value("${my.flag.test3}")
private String test3;
#Override
public void run(String... args) throws Exception {
System.out.println(test3); //true
}
}
See also
Creating a Custom Starter with Spring Boot
I have a very simple Spring Boot application with classes detailed below.
My problem is with the application.properties file and how they get auto-configured. I'm trying to get Groovy Templates to update in dev by setting 'spring.groovy.template.cache: false', however this is not working. I added two more properties to see if the application.properties file was being read. The 'logging.level.org.springframework.web: ERROR' still results in INFO level messages printed to the console. However, some.prop is read correctly into the MyBean class on application start.
Is there a configuration declaration I'm missing for these properties?
src/main/resources/application.properties:
spring.groovy.template.cache: false
logging.level.org.springframework.web: ERROR
some.prop: bob
src/main/java/sample/MyBean.java:
#Component
public class MyBean {
#Value("${some.prop}")
private String prop;
public MyBean() {}
#PostConstruct
public void init() {
System.out.println("================== " + prop + "================== ");
}
}
and src/main/java/sample/Application.java:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
and src/main/java/sample/com/example/MainController.java
#Controller
public class MainController {
#RequestMapping(value="/login", method = RequestMethod.GET)
public ModelAndView risk(#RequestParam Optional<String> error) {
return new ModelAndView("views/login", "error", error);
}
}
It seems you missing scanned your package "sample". Please make sure that you have scanned it.
#ComponentScan({
"sample" })
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Also, your application.properties is right. No problem with it.
It appears the solution was much simpler than I thought:
gradle bootRun
should be used to hot reload templates
gradle run does not work (all compiled classes are just built in build/ )
I need to be able to have my program exit with an error code so that the scheduler that initiated the program can know that it failed. Currently, I am running my job via SpringApplicationBuilder.
#SpringBootApplication
#EnableBatchProcessing
#Slf4j
public class WeeklyImportApplication extends DefaultBatchConfigurer {
...
public static void main(String[] args) {
handleArguments(args);
new SpringApplicationBuilder(WeeklyImportApplication.class).listeners(new CustomLoggingConfigurationApplicationListener(logConfigurer)).run(args);
finished();
}
#Bean
public Job weeklyImport(JobBuilderFactory jobs, Step determineTableName, Step determineColumnNames, Step readAccessDb) {
return jobs.get("weeklyImport").incrementer(new RunIdIncrementer()).flow(determineTableName).next(determineColumnNames).next(readAccessDb).end().build();
}
#Bean
public Step determineTableName(StepBuilderFactory stepBuilderFactory, ItemReader<String> tableNameReader, TableNameWriter tableNameWriter) {
return stepBuilderFactory.get("determineTableName").<String, String> chunk(100).reader(tableNameReader).writer(tableNameWriter).build();
}
#Bean
public Step determineColumnNames(StepBuilderFactory stepBuilderFactory, ItemReader<String> columnNamesReader, ColumnNamesWriter columnNamesWriter) {
return stepBuilderFactory.get("determineColumnNames").<String, String> chunk(1000).reader(columnNamesReader).writer(columnNamesWriter).build();
}
#Bean
public Step readAccessDb(StepBuilderFactory stepBuilderFactory, ItemReader<WeeklyStoreItem> importReader, ItemWriter<WeeklyStoreItem> weeklyStoreItemWriter, PlatformTransactionManager legacyTransactionManager) {
return stepBuilderFactory.get("readAccessDb").<WeeklyStoreItem, WeeklyStoreItem> chunk(chunkSize).reader(importReader).writer(weeklyStoreItemWriter).transactionManager(legacyTransactionManager).build();
}
...
At any point in the job execution if any step fails, I want to be able to exit out and do two things:
Move the file being processed to specfic folder.
Have the scheduler know via the program exit code that an error occurred.
Right now, the job will exit when an uncaught exception occurs, which is partially what I want, but Spring handles the exception, logs it, and then exits gracefully back to my main method. At that point, I'm not sure how to capture whether the job run was truly successful.
I had the same problem. I managed to solve partially like this (Dave Sayer outlined this idea already in comment).
First I created this created listener:
import org.springframework.batch.core.ExitStatus;
import org.springframework.boot.autoconfigure.batch.JobExecutionEvent;
import org.springframework.context.ApplicationListener;
public class JobResultListener implements
ApplicationListener<JobExecutionEvent> {
private ExitStatus jobExitStatus;
public ExitStatus getJobExitStatus() {
return jobExitStatus;
}
#Override
public void onApplicationEvent(JobExecutionEvent event) {
jobExitStatus = event.getJobExecution().getExitStatus();
}
}
Than I used it to get job execution status this way:
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication springApplication =
new SpringApplication(Application.class);
JobResultListener jobResultListener = new JobResultListener();
springApplication.addListeners(jobResultListener);
springApplication.run(args);
if (!ExitStatus.COMPLETED.equals(jobResultListener.getJobExitStatus())) {
throw new IllegalStateException("Job failed");
}
}
}
But for some reason I wasn't able to get instances of exceptions from injected event. But as you mentioned, error is logged into app logs so this mechanism was enough for me to inform called of my process about error with error code != 0.
BTW, I observed that some error penetrated from springApplication.run(args) call, but not sure why not all. Also I didn't find anything mentioned in Spring Boot docs
My Spring Boot application is not a web server, but it's a server using custom protocol (using Camel in this case).
But Spring Boot immediately stops (gracefully) after started. How do I prevent this?
I'd like the app to stop if Ctrl+C or programmatically.
#CompileStatic
#Configuration
class CamelConfig {
#Bean
CamelContextFactoryBean camelContext() {
final camelContextFactory = new CamelContextFactoryBean()
camelContextFactory.id = 'camelContext'
camelContextFactory
}
}
I found the solution, using org.springframework.boot.CommandLineRunner + Thread.currentThread().join(), e.g.:
(note: code below is in Groovy, not Java)
package id.ac.itb.lumen.social
import org.slf4j.LoggerFactory
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
#SpringBootApplication
class LumenSocialApplication implements CommandLineRunner {
private static final log = LoggerFactory.getLogger(LumenSocialApplication.class)
static void main(String[] args) {
SpringApplication.run LumenSocialApplication, args
}
#Override
void run(String... args) throws Exception {
log.info('Joining thread, you can press Ctrl+C to shutdown application')
Thread.currentThread().join()
}
}
As of Apache Camel 2.17 there is a cleaner answer. To quote http://camel.apache.org/spring-boot.html:
To keep the main thread blocked so that Camel stays up, either include the spring-boot-starter-web dependency, or add camel.springboot.main-run-controller=true to your application.properties or application.yml file.
You will want the following dependency too:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>2.17.0</version>
</dependency>
Clearly replace <version>2.17.0</version> or use the camel BOM to import dependency-management information for consistency.
An example implementation using a CountDownLatch:
#Bean
public CountDownLatch closeLatch() {
return new CountDownLatch(1);
}
public static void main(String... args) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(MyApp.class, args);
final CountDownLatch closeLatch = ctx.getBean(CountDownLatch.class);
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
closeLatch.countDown();
}
});
closeLatch.await();
}
Now to stop your application, you can look up the process ID and issue a kill command from the console:
kill <PID>
Spring Boot leaves the task of running the application to the protocol around which the application is implemented. See, for example, this guide:
Also required are some housekeeping objects like a CountDownLatch to keep the main thread alive...
So the way of running a Camel service, for example, would to be to run Camel as a standalone application from your main Spring Boot application class.
This is now made even simpler.
Just add camel.springboot.main-run-controller=true to your application.properties
All threads are completed, the program will close automatically.
So, register an empty task with #Scheduled will create a loop thread to prevent shutdown.
file application.yml
spring:
main:
web-application-type: none
file DemoApplication.java
#SpringBootApplication
#EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
file KeepAlive.java
#Component
public class KeepAlive {
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
#Scheduled(fixedRate = 1 * 1000 * 60) // 1 minute
public void reportCurrentTime() {
log.info("Keepalive at time {}", dateFormat.format(new Date()));
}
}
My project is NON WEB Spirng Boot.
My elegant solution is create a daemon thread by CommandLineRunner.
Then, Application do not shutdown immediately.
#Bean
public CommandLineRunner deQueue() {
return args -> {
Thread daemonThread;
consumer.connect(3);
daemonThread = new Thread(() -> {
try {
consumer.work();
} catch (InterruptedException e) {
logger.info("daemon thread is interrupted", e);
}
});
daemonThread.setDaemon(true);
daemonThread.start();
};
}
To keep the java process alive when not deploying a web application set the webEnvironment property to false like so:
SpringApplication sa = new SpringApplication();
sa.setWebEnvironment(false); //important
ApplicationContext ctx = sa.run(ApplicationMain.class, args);
for springboot app to run continously it has to be run in a container, otherwise it is just like any java app all threads are done it finishes,
you can add
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
and it will turn it into webapp, if not you are responsible keeping it alive in your implementation