I want to create an auto-generated resource file similar to R from android. I know how to do the parsing and creation of the file and essentially creating a new class file. What I don't know how to do is start this auto-generation process.
So, using eclipse (although if there is a way to make this happen in an agnostic fashion, I would prefer it), how can I trigger an auto-generation session to read a properties file and create a .java file holding static variables and the "keys" from this parsed file, that I can then reference from my code?
A few examples of how to generate java source files have already been provided. It should be relatively easy to read the properties file and invoke one of these APIs.
To trigger the code generation process, you need to add a custom build step. With Ant, just add a custom task. And then hook it up to an Eclipse Builder: project Properties -> Builders -> New.
Subsequently, Eclipse should find and refresh this file on its own. If it doesn't, then check your configs: Preferences -> General -> Workspace -> find "Refresh using native hooks or polling" and similar ones and check them. (Note that I'm not 100% sure that this last part will work.)
The path of least resistance is to run this build step separately. If your properties file is not changing that often, then it shouldn't be that big a deal. This is similar to what you'd do if you use Protocol Buffers, JAXB, wsdl2java, etc. If you want everything to work magically like R.java does, then you probably have to do something a little more complicated:
- Try to use Eclipse Builder options to control when the Ant task is executed
- If you can't figure that out, then I'd check out how Eclipse hooks up to the above projects (that is, to Protocol Buffers, JAXB, wsdl2java, etc.)
- Look at the ADT custom PreCompilerBuilder class
- Check out the build-helper-plugin
It is common to use a ResourceBundle to create an object that allows you to lookup properties by key. You can learn about the ResourceBundle on the Java Trail.
The basic idea is that you have a text file with a .properties extension. You point the ResourceBundle object to that file. You can then use the getString() or getObject() method passing in the key to the property you want. That is all there is to it. You just need to load the ResourceBundle when you start your program (or sometime before you need it).
If you create you own class that has a ResourceBundle as a member value, you can use a simple bit of code like this to have a simple get() method to get the property value:
public String get(String aString)
{
String sVal = null;
try
{
sVal = (String)myProperties.getObject(aString);
}
catch (MissingResourceException e)
{
log.debug("Missing Property Value: "+aString);
}
return sVal;
}
I hope that is useful.
Related
I am trying to create a plugin to generate some java code and write back to the main source module. I was able to create a some simple pojo class using JavaPoet and write to the src/main/java.
To make this useful, it should read the code from src/maim/java folder and analyze the classes using reflection. Look for some annotation then generate some codes. Do I use the SourceTask for this case. Looked like I can only access the classes by the files. Is that possible to read the java classes as the class and using reflection analyze the class?
Since you specified what you want to do:
You'll need to implement an annotation processor. This has absolutely nothing to do with gradle, and a gradle plugin is actually the wrong way to go about this. Please look into Java Annotation Processor and come back with more questions if any come up.
With JavaForger you can read input classes and generate sourcecode based on that. It also provides an API to insert it into existing classes or create new classes based on the input file. In contrast to JavaPoet, JavaForger has a clear separation between code to be generated and settings on where and how to insert it. An example of a template for a pojo can look like this:
public class ${class.name}Data {
<#list fields as field>
private ${field.type} ${field.name};
</#list>
<#list fields as field>
public ${field.type} ${field.getter}() {
return ${field.name};
}
public void ${field.setter}(${field.type} ${field.name}) {
this.${field.name} = ${field.name};
}
</#list>
}
The example below uses a template called "myTemplate.javat" and adds some extra settings like creating the file if it does not exist and changing the path where the file will be created from */path/* to */pathToDto/*. The the path to the input class is given to read the class name and fields and more.
JavaForgerConfiguration config = JavaForgerConfiguration.builder()
.withTemplate("myTemplate.javat")
.withCreateFileIfNotExists(true)
.withMergeClassProvider(ClassProvider.fromInputClass(s -> s.replace("path", "pathToPojo")))
.build();
JavaForger.execute(config, "MyProject/path/inputFile.java");
If you are looking for a framework that allows changing the code more programatticaly you can also look at JavaParser. With this framework you can construct an abstract syntax tree from a java class and make changes to it.
I am building a dynamic Web Project (in Eclipse with Tomcat as server) using servlets and JSPs. The general purpose of the project is to let the user solve small code problems. In order to do that I take a method written by the user and use it to build a java file that I can run via Reflection. The problem I can't seem to figure out is that Tomcat (or Eclipse?) does not update the file at runtime. So when I create the file using the code of the current user and try to compile it, my program always executes the file as it was when I started the server using the code of the previous user. How can I tell it to update the file before running it?
Edit:
That's how I create the file:
public boolean writeFile() {
try {
PrintWriter writer = new PrintWriter(relativePath + "src\\testfiles\\TestFile.java");
writer.print(content);
writer.close();
return true; }...
Here I call the writer and try running the file:
FileWriter writer = new FileWriter(content);
if(writer.writeFile()){
Class<?> TestFile;
Method m;
try {
TestFile = cl.loadClass("testfiles.TestFile");
m = TestFile.getDeclaredMethod("testSolution");
m.invoke(null);
Thanks in advance!
Ok, it's now clear what the issue is. Your issue is not with Tomcat not reloading the file, but with the classloader not reloading the class.
Normal classloaders will only load a class once, and keep it cached forever. The only way for a class to get unloaded is by its classloader being garbage collected. To reload a class you either have to use a different classloader each time (with the previous one getting garbage collected or you'll run out of memory), or to have a custom loader thar doesn't cache.
See this article for an implementation of a custom classloader that does what you want.
You could theoretically just have a new class each time (by changing its name on each save) but you'd eventually run out of memory.
Maybe the easiest way is to instantiate a new classloader in the method itself, load a class, run the method on it, and don't keep any other references to the loader or the class. That way, you'll get a freshly loaded class each time, and the old instances of both classes and loaders will get garbage collected.
UPDATE: I was operating under the assumption that you already know how to compile a class at runtime but, based on the comments, that's not the case.
A classloader can, of course, only load a compiled class, so a source directly is not very useful.
Java internally provides a a compiler interface under javax.tools.JavaCompiler, but it's not easy to use and requires a different handling of Java versions before and after Java 9. So it is much easier to use a library like jOOR that hides the gory parts:
Class clazz = Reflect.compile("com.example.Test",
"package com.example;" +
"public class Test {\n" +
" public String hello() {\n" +
" return \"hello\";\n" +
" }\n" +
" }")
.type();
Instead of type() to simply get the class, you can actually keep using jOOR's fluent reflection API to trigger the methods on the generated class or whatever it is you'd normally do via regular reflection.
For direct JavaCompiler usage, see e.g. this or, even better, jOOR's source code.
I have a spring boot application with /resources/application.properties, when deployed there is a application.properties next to the jar (and the one inside it).
Somewhere in the code I use ResourceBundle.getBundle("application").getString("x") but that always return the value defined in the properties inside the jar.
My objective is to get the value from the external file if it exists, if not then I need to get the value from inside the jar. Just like the default springs behavior but I'm not able to achieve this.
edit:
You can either use the solution in the correct answer below or you can rely on springs Environment by autowiring it and using getProperty()
ResourceBundle.getBundle(String baseName) relies on the classloader and not directly the file system to find the resource.
This is equivalent to invoke the overloaded one :
getBundle(baseName, Locale.getDefault(), this.getClass().getClassLoader()),
So your result is expected.
What you look for is PropertyResourceBundle.
To create an instance of you need either a Reader or an InputStream.
You could load both external and internal files.
ResourceBundle internalProps = ResourceBundle.getBundle("application");
ResourceBundle externalProps = new PropertyResourceBundle(Files.newInputStream(Paths.get("application.properties"));
You could so use the external ResourceBundle in first intention and the second one as fallback.
I have a configuration (config.properties) something like
app.rootDir=
app.reportDir=${app.rootDir}/report
The app.rootDir is not a fixed parameter and it must be initialized by external module. I need keep the ${app.reportDir} keep dynamic reference to ${app.rootDir}.
Use pseudo code to illustrate the problem:
// Init the root dir as '/usr/app'
config.setValue('app.rootDir','/usr/app');
// I need the reportDir to be '/usr/app/report'
String reportDir = config.getValue('app.reportDir');
I can write some codes to get this feature but I'd like to know if there is any existing library do this?
I can use properties, yaml, or json as configuration file type, according to the library availability.
I have a properties file (under rsources folder) in which I'm stocking a variable (key=value),
I need to update it when a user insert a new value or update the older one , so can I do it?
I have doubts because it's a web application so it' simply a war deployed in the server. So how it is possible to access the .properties file and change it directly from the code?
If it's not possible, is there another solution?
Perhaps the user could provide an overriding properties file in the filesystem, whose values would override the packaged default properties file.
Check out Apache Commons Configuration, which permits this capability.
Often you want to provide a base set of configuration values, but
allow the user to easily override them for their specific environment.
Well one way is to hard code the default values into your code, and
have then provide a property file that overrides this. However, this
is a very rigid way of doing things. Instead, with the
CompositeConfiguration you can provide many different ways of setting
up a configuration.
Instead of modifying a properties file, you can create a new table in your database (e.g T_PROPERTIES) and add/modify rows in the table.
Define the table with 2 column, key and value and change the records accordingly.
You can let the user write to a properties file, but I don't think it's very clean to do.
There is a class called "Properties" in the java.util package, you can use this class to load a representation of a physical properties file from your webapplication.
for example to load a properties file you could use following code:
public void loadProps(File pfile) throws IOException {
Properties props = new Properties();
FileInputStream fis = new FileInputStream(propsFile);
props.load(fis);
fis.close();
}
Now you can just use built in commands to manipulate the file:
-setProperty(String key, String value);
-get(Object key);
After you're done with it you can just call the save method on the properties Object.
You will need an OutputStream for that.