Run -jar with args and use args in Spring - java

I need to run jar with static parameter, for example, java -jar myapp-1.0.0.jar /path/static/myfolder, and use this path "/path/static/myfolder" in my code. So, I know how to get this parameters in PSVM (String..args), but how to get it in Spring? Is there annotation for this or may be in properties file>

For example: java -jar -Dtest=abcd myapp-1.0.0.jar.
You can use System.getProperty("test") to get value.
-D{parameter} needs to be before that jar

It is really answer for my question:
create special #Component that implements CommandLineRunner and override method Run, like this:
#Component public class StartupPrintRunner implements CommandLineRunner {
private String folder;
#Override public void run(String... args) throws Exception {
this.folder = Arrays.deepToString(args); }
}
Inject bean StartupPrintRunner where you need and get parameters. It returns String looks like an array: [parameter1, parameter2.., parameter n] and we can parse it to get necessary paarameter.

Related

Spring Boot - Could not find a suitable constructor error

Hi I am trying to read a file as an argument from the main class and accessing the argument in another class in Spring boot.
The main class looks like this
public class DemorestApplication extends SpringBootServletInitializer {
public static void main(String[] args) throws IOException {
new DemorestApplication().configure(new SpringApplicationBuilder(DemorestApplication.class)).run(args);
new UsersResources(args[0]);
}
}
And I am passing an argument to another class named UsersResources constructor
#Path("/sample")
public class UsersResources {
private String value;
UsersResources(String value){
this.value=value;
}
//new code
#GET
#Path("Data/file/{path}")
#Produces("application/json")
public Map<String, Map<String, List<Map<String, Map<String, String>>>>> getApplicationName1(#PathParam("path") String path) throws IOException {
ReadExceldy prop = new ReadExceldy();
FileInputStream Fs = new FileInputStream(System.getProperty("user.dir")+"\\"+value);
Properties properties = new Properties();
properties.load(Fs);
String Loc=properties.getProperty("filepath");
String Na=path;
String filename=Loc+Na;
return prop.fileToJson(filename);
}
}
I'm trying to run this code but it's throwing an error saying
java.lang.NoSuchMethodException: Could not find a suitable constructor in com.springsampleapplication.UsersResources class
at org.glassfish.jersey.internal.inject.JerseyClassAnalyzer.getConstructor(JerseyClassAnalyzer.java:192) ~[jersey-common-2.25.1.jar:na]
The issue might be with Spring trying to initialize UsersResources class and expecting a constructor with no arguments, when you have it only with a String parameter. Try adding such constructor: public UsersResources() {}
Other thought that came into mind is that this could be because of UsersResources constructor not having a visibility modifier (it means it is package protected), you could try adding public visibility modifier to it (though Spring should be able to initialize even private constructors). Are the DemorestApplication and UsersResources classes in the same package though? As otherwise the code should not compile, since UsersResources(String value) is not visible outside of the package it is in. But the error is a bit different in such case: The constructor xxx is undefined, so probably this is not the case.
Since it seems you are using Jersey you need to have a public constructor:
Root resource classes are instantiated by the JAX-RS runtime and MUST have a public constructor for which the JAX-RS runtime can provide all parameter values. Note that a zero argument constructor is permissible under this rule.
See How to register a static class in Jersey?
Since you have defined a constructor in your class, no default constructor is generated. A work around is to make this class a Spring component with #Component et al.

Load spring-boot properties from json file

Is it possible to load spring-boot config from a .json file as opposed to .yaml or .properties? From looking at the documentation, this isn't supported out of the box - I'm wondering if it's possible and if so how one would go about doing it?
As noted in docs and on GitHub
YAML is a superset of JSON
So you can just create the following class in your Spring Boot project:
public class JsonPropertySourceLoader extends YamlPropertySourceLoader {
#Override
public String[] getFileExtensions() {
return new String[]{"json"};
}
}
Then create a file:
/src/main/resources/META-INF/spring.factories
with the following content:
org.springframework.boot.env.PropertySourceLoader=\
io.myapp.JsonPropertySourceLoader
And your Spring application is ready to load JSON configurations from application.json. The priority will be: .properties -> .yaml -> .json
If you have multiple apps, you can create a jar with the shared PropertySourceLoader and spring.factories file in order to include it to any project you need.
The spring boot way:
#EnableAutoConfiguration
#Configuration
#PropertySource(value = { "classpath:/properties/config.default.json" }, factory=SpringBootTest.JsonLoader.class )
public class SpringBootTest extends SpringBootServletInitializer {
#Bean
public Object test(Environment e) {
System.out.println(e.getProperty("test"));
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootTest.class);
}
public static class JsonLoader implements PropertySourceFactory {
#Override
public org.springframework.core.env.PropertySource<?> createPropertySource(String name,
EncodedResource resource) throws IOException {
Map readValue = new ObjectMapper().readValue(resource.getInputStream(), Map.class);
return new MapPropertySource("json-source", readValue);
}
}
}
Define your own PropertySourceFactory and hook it in via the #PropertySource annotation. Read the resource, set the properties, use them anywhere.
Only thing is, how do you translate nested properties. The Spring way to do that (by the way you can define Json also as a variable for properties, see: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html)
is to translate nested properties as such:
{"test": { "test2" : "x" } }
Becomes:
test.test2.x
Hope that helps,
Artur
The SPRING_APPLICATION_JSON properties can be supplied on the command line with an environment variable. For example, you could use the following line in a UN*X shell:
$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar
myapp.jar
In the preceding example, you end up with acme.name=test in the Spring Environment. You can also supply the JSON as spring.application.json in a System property, as shown in the following example:
$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
You can also supply the JSON by using a command line argument, as shown in the following example:
$ java -jar myapp.jar --spring.application.json='{"name":"test"}'
You can also supply the JSON as a JNDI variable, as follows:
java:comp/env/spring.application.json.
Reference documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
2 steps
public String asYaml(String jsonString) throws JsonProcessingException, IOException {
// parse JSON
JsonNode jsonNodeTree = new ObjectMapper().readTree(jsonString);
// save it as YAML
String jsonAsYaml = new YAMLMapper().writeValueAsString(jsonNodeTree);
return jsonAsYaml;
}
Got from the post
and
public class YamlFileApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
try {
Resource resource = applicationContext.getResource("classpath:file.yml");
YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
PropertySource<?> yamlTestProperties = yamlTestProperties = sourceLoader.load("yamlTestProperties", resource, null);
applicationContext.getEnvironment().getPropertySources().addFirst(yamlTestProperties);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Got from the post
So you can combine both. Load your json as resource and convert to yaml and then add to Environment all the found properties

Override yml configuration in spring-boot with command line arguments not work

I have a spring-boot application. I want to override some properties which was configuration in application.yml when executing the jar.
My code like this:
#Service
public class CommandService {
#Value("${name:defaultName}")
private String name;
public void print() {
System.out.println(name);
}
}
And the Application.java is
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
private CommandService commandService;
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Override
public void run(String... args) throws Exception {
commandService.print();
}
}
The application.yml
name: nameInApplication
when I excute the command like that:
java -jar app.jar --name=nameInCommand
It's not work
The Second command also not work:
java -Dname=nameInCommand2 -jar app.jar
But if the application.yml not have config the name, the second command will work well and the first command also not work any way.
This is a few months old, but I'm going to answer it because I just ran into this issue and found your question via google and Ivan's comment jogged my memory. Since he did not elaborate and I'm not sure if you solved your issue (you probably have by now), you are going to want to change:
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
to
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
That simple. The arguments before were never going anywhere when they got passed in thus not overriding your application.yml property. So just in case you haven't figured it out or if someone stumbled upon this question like I did, that is what Ivan meant by
do you pass command line arguments when starting
SpringApplication.run(...)?

Pass parameters to JUnit test from the TestSuite class

I want to create 2 JUnit TestSuites. They both utilize the same test classes, but they should each use different parameters. For example, in test suite A, I want my data to be collected from file A and to be written to database A. In test suite B, I want my data to be collected from file B and to be written to databaseB.
The reason I use testSuites for this is because:
I can put all the specific parameters in the testsuite classes
I can reuse the testclasses
I can choose which testsuite to run. I do not want all tests to always run with all possible paramaters!
The problem is I cannot really pass the parameters. I understand the way the Parameterized class works with JUnit, but it does not allow point 3 in the list above. If I use the code below it will run my test class with both databse connections, which is not what I want to achieve.
#RunWith(value = Parameterized.class)
public class TestCheckData
{
private File file;
private DatabaseSource databaseSource;
public TestCheckData(File file, DatabaseSource databaseSource)
{
this.file = file;
this.databaseSource = databaseSource;
}
#Parameters
public static Iterable<Object[]> data1()
{
return Arrays.asList(new Object[][]
{
{ TestSuiteA.DATA_FILE_A, TestSuite1.DATABASE_A },
{ TestSuiteB.DATA_FILE_B, TestSuite1.DATABASE_B }
});
}
I already find some way of passing configurations in a spring context in this question, but I'm not using any special framework.
Well, this would be a little unconventional, but you could add a different Test class to the beginning of each suite run that would set the parameters you want to use for that test. So you'd have classes like:
public abstract class StaticParameters {
public static File dataFileToUse = null;
public static DatabaseSource databaseToUse = null;
}
public class Suite1Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_A;
databaseToUse = DATABASE_A;
}
}
public class Suite2Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_B;
databaseToUse = DATABASE_B;
}
}
Then you'd just make Suite1Params or Suite2Params the first in your suite list. You might have to add a fake #Test entry to the params classes, I'm not sure if the Suite runner requires that.
You could modify the tests so that they get the parameters from a config file. This way you would always only have 1 Suite.
The path of the config file can be looked up via a System property.
Then on the invocation of the test suite, you could pass in a different config file by changing the property using the -D option on the JVM.
So for example if you named the proprerty env.properties then your command would be:
%java -Denv.properties=prod.config runMyTests
or
%java -Denv.properties=dev.config runMyTests
etc

how to update jar class files through programming

I have a jar file which consisting of multiple class files.
Main class file's java file is like
class Main
{
public void setUserType()
{
String utype="user";
}
}
now i want to update the Main class file with usertype "user" to "admin" and recreate the jar file
can you suggest me how can i do this so that the new jar file should have usertype "admin"
These changes should be done through programming not through editor like netbeans or eclipse
Instead of hardcoding things, you should define your code to accept properties either via property file or via environment/System property or command line argument when you start your application like below:
class Main {
public void setUserType(String value) {
String utype=value;//or use System.getProperty("value");if you used -Dvalue=admin in command line for example.
}
public static void main(String args[]) {//see main accepts command line argument
if (args.length == 0) {
throw new IllegalArgumentException("Required parameters are missing");
}
Main main = new Main();
main(args[0]);//just pass whatever you pass as system parameter when you start your application
//if you dont want to pass string across multiple methods and classes just use System.setProperty("value", args[0]); and use it like System.getProperty("value") to access it from anywhere without actually passing this string across.
}
}
And run it with user/admin as you like:
java Main admin
java Main user

Categories

Resources