I stumbled upon this question from a while back, explaining how to get a Spring Shell application to exit after calling it from the command line with a single command. However, testing this in 2.0.0 with Spring Boot, it does not seem to be the case any more that invoking the JAR with command arguments will execute that command and then exit. The shell just starts as normal without executing the supplied command. Is it still possible to do this? If not, would it be possible to pass the arguments from the JAR execution to Spring Shell and then trigger an exit after execution?
For example, let's say I have a command, import that has a couple options. It could be run in the shell like this:
$ java -jar my-app.jar
> import -f /path/to/file.txt --overwrite
Successfully imported 'file.txt'
> exit
But it would be nice to be able to simply execute and exit, for the sake of building a script that can utilize this function:
$ java -jar my-app.jar import -f /path/to/file.txt --overwrite
Successfully imported 'file.txt'
Run it with #my-script, like so:
java -jar my-app.jar #my-script
Where my-script is a file with your commands:
import -f /path/to/file.txt --overwrite
Just to add, I found another way of doing this, doesn't give you the option to run in interactive mode but using your profiles above you can of course swap the configuration. Please note I'm using lombok and jool (just in case anyone copy pastes and gets funny issues!)
Entry
#SpringBootApplication
public class Righter {
public static void main(String[] args) {
SpringApplication.run(Righter.class, args);
}
#Bean
public ApplicationRunner shellRunner(Shell shell) {
return new NonInteractiveShellRunner(shell);
}
Application runner:
#Order(0)
public class NonInteractiveShellRunner implements ApplicationRunner{
private final Shell shell;
public NonInteractiveShellRunner(Shell shell) {
this.shell = shell;
}
#Override
public void run(ApplicationArguments args) throws Exception {
shell.run(new CommandInputProvider(args.getSourceArgs()));
}
public static class PredefinedInputProvider implements InputProvider{
private final Input input;
private boolean commandExecuted = false;
public PredefinedInputProvider(String[] args) {
this.input = new PredefinedInput(args);
}
#Override
public Input readInput() {
if (!commandExecuted){
commandExecuted=true;
return input;
}
return new PredefinedInput(new String[]{"exit"});
}
#AllArgsConstructor
private static class PredefinedInput implements Input{
private final String[] args;
#Override
public String rawText() {
return Seq.of(args).toString(" ");
}
#Override
public List<String> words(){
return Arrays.asList(args);
}
}
}
}
A way to add a single command run mode without excluding interactive mode and script mode (Tested on spring-shell-starter::2.0.0.RELEASE).
Create a runner by analogy with ScriptShellApplicationRunner.
// Runs before ScriptShellApplicationRunner and InteractiveShellApplicationRunner
#Order(InteractiveShellApplicationRunner.PRECEDENCE - 200)
public class SingleCommandApplicationRunner implements ApplicationRunner {
private final Parser parser;
private final Shell shell;
private final ConfigurableEnvironment environment;
private final Set<String> allCommandNames;
public SingleCommandApplicationRunner(
Parser parser,
Shell shell,
ConfigurableEnvironment environment,
Set<CustomCommand> customCommands
) {
this.parser = parser;
this.shell = shell;
this.environment = environment;
this.allCommandNames = buildAllCommandNames(customCommands);
}
private Set<String> buildAllCommandNames(Collection<CustomCommand> customCommands) {
final Set<String> result = new HashSet<>();
customCommands.stream().map(CustomCommand::keys).flatMap(Collection::stream).forEach(result::add);
// default spring shell commands
result.addAll(asList("clear", "exit", "quit", "help", "script", "stacktrace"));
return result;
}
#Override
public void run(ApplicationArguments args) throws Exception {
final boolean singleCommand = haveCommand(args.getSourceArgs());
if (singleCommand) {
InteractiveShellApplicationRunner.disable(environment);
final String fullArgs = join(" ", args.getSourceArgs());
try (Reader reader = new StringReader(fullArgs);
FileInputProvider inputProvider = new FileInputProvider(reader, parser)) {
shell.run(inputProvider);
}
}
}
private boolean haveCommand(String... args) {
for (String arg : args) {
if (allCommandNames.contains(arg)) {
return true;
}
}
return false;
}
}
Register the runner as bean.
#Configuration
class ContextConfiguration {
#Autowired
private Shell shell;
#Bean
SingleCommandApplicationRunner singleCommandApplicationRunner(
Parser parser,
ConfigurableEnvironment environment,
Set<CustomCommand> customCommands
) {
return new SingleCommandApplicationRunner(parser, shell, environment, customCommands);
}
}
So that the runner starts only when the command is sent, we create an interface.
public interface CustomCommand {
Collection<String> keys();
}
Implement CustomCommand interface in each commands.
#ShellComponent
#RequiredArgsConstructor
class MyCommand implements CustomCommand {
private static final String KEY = "my-command";
#Override
public Collection<String> keys() {
return singletonList(KEY);
}
#ShellMethod(key = KEY, value = "My custom command.")
public AttributedString version() {
return "Hello, single command mode!";
}
}
Done!
Run in interactive mode:
java -jar myApp.jar
// 2021-01-14 19:28:16.911 INFO 67313 --- [main] com.nao4j.example.Application: Starting Application v1.0.0 using Java 1.8.0_275 on Apple-MacBook-Pro-15.local with PID 67313 (/Users/nao4j/example/target/myApp.jar started by nao4j in /Users/nao4j/example/target)
// 2021-01-14 19:28:16.916 INFO 67313 --- [main] com.nao4j.example.Application: No active profile set, falling back to default profiles: default
// 2021-01-14 19:28:18.227 INFO 67313 --- [main] com.nao4j.example.Application: Started Application in 2.179 seconds (JVM running for 2.796)
// shell:>my-command
// Hello, single command mode!
Run script from file script.txt (contains text "my-command"):
java -jar myApp.jar #script.txt
// 2021-01-14 19:28:16.911 INFO 67313 --- [main] com.nao4j.example.Application: Starting Application v1.0.0 using Java 1.8.0_275 on Apple-MacBook-Pro-15.local with PID 67313 (/Users/nao4j/example/target/myApp.jar started by nao4j in /Users/nao4j/example/target)
// 2021-01-14 19:28:16.916 INFO 67313 --- [main] com.nao4j.example.Application: No active profile set, falling back to default profiles: default
// 2021-01-14 19:28:18.227 INFO 67313 --- [main] com.nao4j.example.Application: Started Application in 2.179 seconds (JVM running for 2.796)
// Hello, single command mode!
Run in single command mode:
java -jar myApp.jar my-command
// 2021-01-14 19:28:16.911 INFO 67313 --- [main] com.nao4j.example.Application: Starting Application v1.0.0 using Java 1.8.0_275 on Apple-MacBook-Pro-15.local with PID 67313 (/Users/nao4j/example/target/myApp.jar started by nao4j in /Users/nao4j/example/target)
// 2021-01-14 19:28:16.916 INFO 67313 --- [main] com.nao4j.example.Application: No active profile set, falling back to default profiles: default
// 2021-01-14 19:28:18.227 INFO 67313 --- [main] com.nao4j.example.Application: Started Application in 2.179 seconds (JVM running for 2.796)
// Hello, single command mode!
In addition to Alex answers, here is the simpler version of NonInteractiveApplicationRunner I made.
#Component
#Order(InteractiveShellApplicationRunner.PRECEDENCE - 100)
class NonInteractiveApplicationRunner implements ApplicationRunner {
private final Shell shell;
private final ConfigurableEnvironment environment;
public NonInteractiveApplicationRunner(Shell shell, ConfigurableEnvironment environment) {
this.shell = shell;
this.environment = environment;
}
#Override
public void run(ApplicationArguments args) {
if (args.getSourceArgs().length > 0) {
InteractiveShellApplicationRunner.disable(environment);
var input = String.join(" ", args.getSourceArgs());
shell.evaluate(() -> input);
shell.evaluate(() -> "exit");
}
}
}
Using #Component, we don't need to add bean method. In addition, using shell.evaluate() method looks much simpler compare to shell.run(...).
I found a nice little work-around. Rather than creating an ApplicationRunner that mimics the v1 behavior (which is tricky, since JLineInputProvider is a private class), I created one that is optionally loaded, based on active Spring profile. I used JCommander to define the CLI parameters, allowing me to have identical commands for the interactive shell and the one-off executions. Running the Spring Boot JAR with no args triggers the interactive shell. Running it with arguments triggers the one-and-done execution.
#Parameters
public class ImportParameters {
#Parameter(names = { "-f", "--file" }, required = true, description = "Data file")
private File file;
#Parameter(names = { "-t", "--type" }, required = true, description = "Data type")
private DataType dataType;
#Parameter(names = { "-o", "--overwrite" }, description = "Flag to overwrite file if it exists")
private Boolean overwrite = false;
/* getters and setters */
}
public class ImportCommandExecutor {
public void run(ImportParameters params) throws Exception {
// import logic goes here
}
}
/* Handles interactive shell command execution */
#ShellComponent
public class JLineInputExecutor {
// All command executors are injected here
#Autowired private ImportCommandExecutor importExecutor;
...
#ShellMethod(key = "import", value = "Imports the a file of a specified type.")
public String importCommand(#ShellOption(optOut = true) ImportParameters params) throws Exception {
importCommandExecutor.run(params);
}
...
}
/* Handles one-off command execution */
public class JCommanderInputExecutor implements ApplicationRunner {
// All command executors are injected here
#Autowired private ImportCommandExecutor importExecutor;
...
#Override
public void run(ApplicationArguments args) throws Exception {
// Create all of the JCommander argument handler objects
BaseParameters baseParameters = new BaseParameters();
ImportParameters importParameters = new ImportParameters();
...
JCommander jc = newBuilder().
.acceptUnknownOptions(true)
.addObject(baseParameters)
.addCommand("import", importParameters)
...
.build();
jc.parse(args);
String mainCommand = jc.getParsedCommand();
if ("import".equals(mainCommand)){
importExecutor.run(importParameters);
} else if (...) {
...
}
}
}
#Configuration
#Profile({"CLI"})
public class CommandLineInterfaceConfiguration {
// All of my command executors are defined as beans here, as well as other required configurations for both modes of execution
#Bean
public ImportCommandExecutor importExecutor (){
return new ImportCommandExecutor();
}
...
}
#Configuration
#Profile({"SINGLE_COMMAND"})
public class SingleCommandConfiguration {
#Bean
public JCommanderInputExecutor commandLineInputExecutor(){
return new JCommanderInputExecutor();
}
}
#SpringBootApplication
public class Application {
public static void main(String[] args) throws IOException {
String[] profiles = getActiveProfiles(args);
SpringApplicationBuilder builder = new SpringApplicationBuilder(Application.class);
builder.bannerMode((Mode.LOG));
builder.web(false);
builder.profiles(profiles);
System.out.println(String.format("Command line arguments: %s Profiles: %s",
Arrays.asList(args), Arrays.asList(profiles)));
builder.run(args);
}
private static String[] getActiveProfiles(String[] args){
return Arrays.asList(args).contains("-X") ? new String[]{"CLI", "SINGLE_COMMAND"} : new String[]{"CLI"};
}
}
So now I can trigger the interactive client by simply running my executable JAR:
java -jar app.jar
> import -f /path/to/file.txt -t GENE -o
> quit()
Or, if I pass the '-X' argument on the command line, the application will execute and then exit:
java -jar app.jar -X import -f /path/to/file.txt -t GENE -o
In linux works this way too:
echo "import -f /path/to/file.txt --overwrite" | java -jar my-app.jar
despite the fact that it ends with a failure; it happens only after the command is successfully executed.
Related
I'm learning for the first time how to use spring batch.
I did my spring conf like this :
#EnableBatchProcessing
#SpringBootApplication
public class BatchChap4Application {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Bean
public Job job(){
return jobBuilderFactory.get("basicJob").start(step1()).build();
}
#Bean
public Step step1(){
return stepBuilderFactory.get("step1")
.tasklet((stepContribution, chunkContext) -> {
System.out.println("Hello World");
return RepeatStatus.FINISHED;
}).build();
}
public static void main(String[] args) {
SpringApplication.run(BatchChap4Application.class, args);
}
}
when I run jar manually from command line with string value , it works perfectly.
java -jar batch.jar executionDate=2021/02/21
but when I tried to change the param type from string to date, it always show zsh: no matches found: executionDate(date)=2021/02/21
java -jar batch.jar executionDate(date)=2021/02/21
got an unexpected output like this :
zsh: no matches found: executionDate(date)=2021/02/21**strong text**
I tried to search for error. unfortunately no answer. I'm using macOS for the development.
please help.
You need to escape the parenthesis:
java -jar batch.jar executionDate\(date\)=2021/02/21
Or pass job parameters between single quotes:
java -jar batch.jar 'executionDate(date)=2021/02/21'
I download a fresh 6.1 broadleaf-commerce and run my local machine via java -javaagent:./admin/target/agents/spring-instrument.jar -jar admin/target/admin.jar successfully on mine macbook. But in my centos 7 I run sudo java -javaagent:./admin/target/agents/spring-instrument.jar -jar admin/target/admin.jar with following error
2020-10-12 13:20:10.838 INFO 2481 --- [ main] c.b.solr.autoconfigure.SolrServer : Syncing solr config file: jar:file:/home/mynewuser/seafood-broadleaf/admin/target/admin.jar!/BOOT-INF/lib/broadleaf-boot-starter-solr-2.2.1-GA.jar!/solr/standalone/solrhome/configsets/fulfillment_order/conf/solrconfig.xml to: /tmp/solr-7.7.2/solr-7.7.2/server/solr/configsets/fulfillment_order/conf/solrconfig.xml
*** [WARN] *** Your Max Processes Limit is currently 62383.
It should be set to 65000 to avoid operational disruption.
If you no longer wish to see this warning, set SOLR_ULIMIT_CHECKS to false in your profile or solr.in.sh
WARNING: Starting Solr as the root user is a security risk and not considered best practice. Exiting.
Please consult the Reference Guide. To override this check, start with argument '-force'
2020-10-12 13:20:11.021 ERROR 2481 --- [ main] c.b.solr.autoconfigure.SolrServer : Problem starting Solr
Here is the source code of solr configuration, I believe it is the place to change the configuration to run with the argument -force in programming way.
package com.community.core.config;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.broadleafcommerce.core.search.service.SearchService;
import org.broadleafcommerce.core.search.service.solr.SolrConfiguration;
import org.broadleafcommerce.core.search.service.solr.SolrSearchServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
*
*
* #author Phillip Verheyden (phillipuniverse)
*/
#Component
public class ApplicationSolrConfiguration {
#Value("${solr.url.primary}")
protected String primaryCatalogSolrUrl;
#Value("${solr.url.reindex}")
protected String reindexCatalogSolrUrl;
#Value("${solr.url.admin}")
protected String adminCatalogSolrUrl;
#Bean
public SolrClient primaryCatalogSolrClient() {
return new HttpSolrClient.Builder(primaryCatalogSolrUrl).build();
}
#Bean
public SolrClient reindexCatalogSolrClient() {
return new HttpSolrClient.Builder(reindexCatalogSolrUrl).build();
}
#Bean
public SolrClient adminCatalogSolrClient() {
return new HttpSolrClient.Builder(adminCatalogSolrUrl).build();
}
#Bean
public SolrConfiguration blCatalogSolrConfiguration() throws IllegalStateException {
return new SolrConfiguration(primaryCatalogSolrClient(), reindexCatalogSolrClient(), adminCatalogSolrClient());
}
#Bean
protected SearchService blSearchService() {
return new SolrSearchServiceImpl();
}
}
Let me preface this by saying you would be better off simply not starting the application as root. If you are in Docker, you can use the USER command to switch to a non-root user.
The Solr server startup in Broadleaf Community is done programmatically via the broadleaf-boot-starter-solr dependency. This is the wrapper around Solr that ties it to the Spring lifecycle. All of the real magic happens in the com.broadleafcommerce.solr.autoconfigure.SolrServer class.
In that class, you will see a startSolr() method. This method is what adds startup arguments to Solr.
In your case, you will need to mostly copy this method wholesale and use cmdLine.addArgument(...) to add additional arguments. Example:
class ForceStartupSolrServer extends SolrServer {
public ForceStartupSolrServer(SolrProperties props) {
super(props);
}
protected void startSolr() {
if (!isRunning()) {
if (!downloadSolrIfApplicable()) {
throw new IllegalStateException("Could not download or expand Solr, see previous logs for more information");
}
stopSolr();
synchConfig();
{
CommandLine cmdLine = new CommandLine(getSolrCommand());
cmdLine.addArgument("start");
cmdLine.addArgument("-p");
cmdLine.addArgument(Integer.toString(props.getPort()));
// START MODIFICATION
cmdLine.addArgument("-force");
// END MODIFICATION
Executor executor = new DefaultExecutor();
PumpStreamHandler streamHandler = new PumpStreamHandler(System.out);
streamHandler.setStopTimeout(1000);
executor.setStreamHandler(streamHandler);
try {
executor.execute(cmdLine);
created = true;
checkCoreStatus();
} catch (IOException e) {
LOG.error("Problem starting Solr", e);
}
}
}
}
}
Then create an #Configuration class to override the blAutoSolrServer bean created by SolrAutoConfiguration (note the specific package requirement for org.broadleafoverrides.config):
package org.broadleafoverrides.config;
public class OverrideConfiguration {
#Bean
public ForceStartupSolrServer blAutoSolrServer(SolrProperties props) {
return new ForceStartupSolrServer(props);
}
}
I'm learning how to use btrace. In order to do that, I created a spring-boot project which contained the following code.
#Controller
public class MainController {
private static Logger logger = LoggerFactory.getLogger(MainController.class);
#ResponseBody
#GetMapping("/testFile")
public Map<String, Object> testFile() throws IOException {
File file = new File("/tmp/a");
if (file.exists()) {
file.delete();
}
file.createNewFile();
return ImmutableMap.of("success", true);
}
}
Then I started the project using mvn spring-boot:run, after which I wrote a btrace script, as follows.
import com.sun.btrace.annotations.*;
import com.sun.btrace.BTraceUtils;
#BTrace
public class HelloWorld {
#OnMethod(clazz = "java.io.File", method = "createNewFile")
public static void onNewFileCreated(String fileName) {
BTraceUtils.println("New file is being created");
BTraceUtils.println(fileName);
}
}
As you can see, this script should print something when java.io.File#createNewFile is called, which is exactly what the above controller does. Then I attached btrace to the running spring-boot project using the following code.
btrace 30716 HelloWorld.java
30716 is the PID of the running spring-boot project. Then I tried accessing http://localhost:8080/testFile, and I got the following extra output from the running spring-boot project.
objc[30857]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/bin/java (0x10e2744c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x1145e24e0). One of the two will be used. Which one is undefined.
2019-01-04 11:24:49.003 INFO 30857 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-01-04 11:24:49.003 INFO 30857 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-01-04 11:24:49.019 INFO 30857 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 16 ms
I was expecting it to output New file is being created, but it didn't. Why? Did I do something wrong?
Your trace method, onNewFileCreated(String fileName), cannot be used to intercept java.io.File.createNewFile() as the signatures don't agree (createNewFile() doesn't take any arguments, while onNewFileCreated() has one). If there are arguments in the trace method (unless they have a BTrace annotation), BTrace will attempt to "bind" them to the arguments in the intercepted method. If it can't do so, it will not successfully intercept that method.
Try
#OnMethod(clazz = "java.io.File", method = "createNewFile")
public static void onNewFileCreated() {
BTraceUtils.println("method createNewFile called");
}
or
#OnMethod(clazz = "java.io.File", method = "createNewFile")
public static void onNewFileCreated(#ProbeMethodName String methodName) {
BTraceUtils.println("method " + methodName + " called");
}
Update 1:
First, what version of the JDK are you using? BTrace doesn't appear to support JDK > 8 (https://github.com/btraceio/btrace/issues/292).
Second, can you try running this tracing script:
import com.sun.btrace.annotations.*;
import com.sun.btrace.BTraceUtils;
#BTrace
public class TracingScript {
#OnMethod(clazz = "java.io.File", method = "createNewFile")
public static void onNewFileCreated(#ProbeMethodName String methodName) {
BTraceUtils.println("method " + methodName + " called");
}
}
against a simple test application:
import java.io.File;
public class FileCreator {
public static void main(String[] args) throws Exception{
for(int i = 0; i < 250; i++) {
File file = new File("C://Temp//file" + i);
if (file.exists()) {
file.delete();
}
file.createNewFile();
Thread.sleep(10000);
}
}
}
This works for me with BTrace 1.3.11.3 (and via the BTrace Workbench JVisualVM Plugin 0.6.8, which is where I usually use BTrace).
I'm using JUnit Jupiter version 5.0.0 (Release version) and I'm trying to use the test discovery feature.
The documentation of Junit can be found in 7.1.1. Discovering Tests from http://junit.org/junit5/docs/5.0.0/user-guide/#launcher-api-discovery
My implementation is:
import static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestIdentifier;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.LoggingListener;
public class MainPrueba {
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
System.out.println("Runing thread INI");
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(
selectPackage("org.package.qabootfx.test.ping")
//,selectClass(QabootfxApplicationTests.class)
)
.filters(
//includeClassNamePatterns(".*Test")
includeClassNamePatterns(".*")
)
.build();
Launcher launcher = LauncherFactory.create();
TestPlan testPlan = launcher.discover(request);
for (TestIdentifier root : testPlan.getRoots()) {
System.out.println("Root: " + root.toString());
for (TestIdentifier test : testPlan.getChildren(root)) {
System.out.println("Found test: " + test.toString());
}
}
// Register a listener of your choice
//TestExecutionListener listener = new SummaryGeneratingListener();
TestExecutionListener listener = LoggingListener.forJavaUtilLogging(); //new LoggingListener();
launcher.registerTestExecutionListeners(listener);
launcher.execute(request);
System.out.println("Runing thread END");
};
new Thread(task).start();
Thread.sleep(5000);
System.out.println("END");
}
}
Examining LoggingListener class implementation we can see that this must print to the console the results. For example:
package org.junit.platform.launcher.listeners;
#API(status = MAINTAINED, since = "1.0")
public class LoggingListener implements TestExecutionListener {
....
#Override
public void testPlanExecutionStarted(TestPlan testPlan) {
log("TestPlan Execution Started: %s", testPlan);
}
#Override
public void testPlanExecutionFinished(TestPlan testPlan) {
log("TestPlan Execution Finished: %s", testPlan);
}
...
}
and my Test class is:
public class QabootfxApplicationTest {
#Test
public void testAbout() {
System.out.println("TEST Execution.... QabootfxApplicationTests.testAbout()");
assertEquals(4, 5, "The optional assertion message is now the last parameter.");
}
}
I'm expecting see in the console something similar to:
2017-09-20 10:53:48.041 INFO 11596 --- TestPlan Execution Started: ....
2017-09-20 10:53:48.041 INFO 11596 --- TestPlan Execution Finished: ....
but I can't see nothing similar to "... TestPlan Execution Started...".
The console output is:
Runing thread INI
Root: TestIdentifier [uniqueId = '[engine:junit-jupiter]', parentId = null, displayName = 'JUnit Jupiter', legacyReportingName = 'JUnit Jupiter', source = null, tags = [], type = CONTAINER]
Found test: TestIdentifier [uniqueId = '[engine:junit-jupiter]/[class:org.package.qabootfx.test.ping.QabootfxApplicationTest]', parentId = '[engine:junit-jupiter]', displayName = 'QabootfxApplicationTest', legacyReportingName = 'org.package.qabootfx.test.ping.QabootfxApplicationTest', source = ClassSource [className = 'org.package.qabootfx.test.ping.QabootfxApplicationTest', filePosition = null], tags = [], type = CONTAINER]
TEST Executon.... QabootfxApplicationTests.testAbout()
Runing thread END
END
Could be a bug? or I'm implementing something wrong?
Why would you expect the listener created by LoggingListener.forJavaUtilLogging() to log anything at log level INFO... when the documentation explicitly states the following?
Create a LoggingListener which delegates to a java.util.logging.Logger using a log level of FINE.
If you want the LoggingListener to log messages at level INFO, you'll have to create it using the other factory method which accepts a log level like this LoggingListener.forJavaUtilLogging(Level.INFO).
I am trying to use dropwizard-sundial and am having trouble with a resource. I'm not sure if it's a classpath issue or if I am failing to register resources properly.
This is my application class' run method:
public void run(DataLoaderApplicationConfiguration configuration, Environment environment) throws Exception {
logger.info("Started DataLoader Application");
final String template = configuration.getTemplate();
environment.healthChecks().register("TemplateHealth", new TemplateHealthCheck(template));
// JOBS
environment.jersey().packages("com.tradier.dataloader.jobs");
}
I get the following error at runtime:
INFO [2015-04-07 15:00:19,737] com.xeiam.sundial.plugins.AnnotationJobTriggerPlugin: Loading annotated jobs from com.tradier.dataloader.jobs.
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.RuntimeException: Unexpected problem: No resource for com/tradier/dataloader/jobs
at org.quartz.classloading.CascadingClassLoadHelper.getJobClasses(CascadingClassLoadHelper.java:217)
at com.xeiam.sundial.plugins.AnnotationJobTriggerPlugin.start(AnnotationJobTriggerPlugin.java:72)
at org.quartz.QuartzScheduler.startPlugins(QuartzScheduler.java:1102)
at org.quartz.QuartzScheduler.start(QuartzScheduler.java:211)
at com.xeiam.sundial.SundialJobScheduler.startScheduler(SundialJobScheduler.java:102)
Check out a working example at https://github.com/timmolter/XDropWizard. It uses annotated jobs. You need to add the package name conatining the annotated jobs in your config.yaml file like this:
sundial:
thread-pool-size: 5
shutdown-on-unload: true
wait-on-shutdown: false
start-delay-seconds: 0
start-scheduler-on-load: true
global-lock-on-load: false
annotated-jobs-package-name: org.knowm.xdropwizard.jobs
If you still are getting an exception, leave a report at: https://github.com/timmolter/dropwizard-sundial/issues.
#Jeyashree Narayanan, the jobs package should not be configured in the application class as you have shown, it can be easily done in the yml file. Here is the explanation in simple steps:
Step 1: Configuration in yml file and the Configuration class
sundial:
thread-pool-size: 10
shutdown-on-unload: true
start-delay-seconds: 0
start-scheduler-on-load: true
global-lock-on-load: false
annotated-jobs-package-name: com.tradier.dataloader.jobs
tasks: [startjob, stopjob]
Configuration Class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class DropwizardSundialConfiguration extends Configuration {
#Valid
#NotNull
public SundialConfiguration sundialConfiguration = new SundialConfiguration();
#JsonProperty("sundial")
public SundialConfiguration getSundialConfiguration() {
return sundialConfiguration;
}
}
Step 2: Add and configure the dropwizard-sundial bundle in the application class.
public class DropwizardSundialApplication extends Application<DropwizardSundialConfiguration> {
private static final Logger logger = LoggerFactory.getLogger(DropwizardSundialApplication.class);
public static void main(String[] args) throws Exception {
new DropwizardSundialApplication().run("server", args[0]);
}
#Override
public void initialize(Bootstrap<DropwizardSundialConfiguration> b) {
b.addBundle(new SundialBundle<DropwizardSundialConfiguration>() {
#Override
public SundialConfiguration getSundialConfiguration(DropwizardSundialConfiguration configuration) {
return configuration.getSundialConfiguration();
}
});
}
}
Step 3: Add the required job classes.
Here is a sample Cron job class:
#CronTrigger(cron = "0 19 13 * * ?")
public class CronJob extends Job {
private static final Logger logger = LoggerFactory.getLogger(CronJob.class);
#Override
public void doRun() throws JobInterruptException {
logger.info("Hello from Cron Job");
}
}
I have also written a blog post and a working application which is available on GitHub with these steps. Please check: http://softwaredevelopercentral.blogspot.com/2019/05/dropwizard-sundial-scheduler-tutorial.html
It appears to be a classpath issue.
From https://github.com/timmolter/Sundial/blob/develop/src/main/java/com/xeiam/sundial/SundialJobScheduler.java#L102:
public static void startScheduler(int threadPoolSize, String annotatedJobsPackageName) {
try {
createScheduler(threadPoolSize, annotatedJobsPackageName);
getScheduler().start(); // ---> Line 102
} catch (SchedulerException e) {
logger.error("COULD NOT START SUNDIAL SCHEDULER!!!", e);
throw new SchedulerStartupException(e);
}
I'm also using Sundial in my dropwizard project, I have all my jobs defined in jobs.xml, Sundial config defined in the .yaml file, and start it as follows:
SundialJobScheduler.startScheduler();
SundialManager sm = new SundialManager(config.getSundialConfiguration(),environment);
environment.lifecycle().manage(sm);