Custom configuration files in Maven application (Java/Scala) - scala

I have a Maven project written in Scala and deployed as a .jar. The project has the typical structure:
project-root
|__src
|__main
| |__scala
| |__resources
|
|__test
|__scala
|__resources
I have many hardcoded values in the application that I'd like to extract to a configuration file.
I could just create a text config file anywhere, read it from the code and use the parameters, but I'm looking for a best practice/approach. I'd like to take this things into account:
Is there a way such that I can change config values and not have to rebuild the .jar? That would be very practical.
Where would be the best location for the file? E. g. create a project-root/src/config directory and put the file there?
Any remarkable reasons about choosing among different formats? I'd go with XML, but I've seen quite a bit of YAML and HOCON around...

If you want to be able to change configuration values without building the jar, you will want your config external to the artifact. There are ways to modify the contents of the built jar but I don't think that is what you are wanting to do. If you want to have config you can change on the fly, keep it out of your jar and reference it on the Classpath at runtime.
If you want to keep it on your artifact, and rebuild when you change values, keep the configuration file in src/main/resources. Maven will automatically package it into your jar for you at the root of the archive.
As for format, that's probably personal preference. Reading YAML usually requires another library such as snakeyml to parse effectively, so if you are trying to keep your library light on dependencies, maybe look at .config or .properties files instead. Otherwise, XML and YAML are always nice for complex configuration.

Related

mybatis-generator: Create non-java files?

I am using the mybatis-generator in a maven project to generate the Java files for a few tables. At the end of the generation, I would like to generate a few non-java files like properties files and resources. However the default generator allows me to generate only XML and Java files. Is there any way to also get the generator to create sql files, SPI definitions and property files for example?
Looking inside the generator, it seems that the Generated java files and XML files go through some further process(formatting et al). Even if I write a custom plugin, I can generate an XML or an sql file only but not a properties files or an sql file. Even if I did, I cannot get the process to finish because the subsequent steps would fail.
Currently, I am getting over these by creating my own files and writing them thru a custom plugin. However, during the plugin execution, the folder target/generates-sources/mybatis-generator is not created yet. Therefore assuming that location to have already been created is ruled out. On the other hand, if I go ahead and create the folder and its internal META-INF/services folder, I am not sure if this will be overwritten at a later stage. In addition, my plugin does not (by virtue of the way the generator initiates plugins), have access to the project root folder. So that is not an option either.
I neither have access to the ShellCallBack, implying that postponing the file creation to a well defined time-point in the build process is also not possible.
So how do I go about creating the service definitions and the additional resource files?
The last resort is to hard-code the project folder or to pump the project folder through a property. This is coming to my rescue now. But clearly, the generated files are being detected by my git client and I have to clean up these files also despite their being dynamic.
Hints please?
Thanks in advance.
Rahul
The generator currently supports Java, Kotlin, and XML file generation. There is an open feature request to support other file types in plugins. You can follow it here: https://github.com/mybatis/generator/issues/752

sbt-assembly: Generate a minimal JAR file

I've been using sbt-assembly to generate standalone JAR file for my scala project. However, I would like to reduce the size of my JAR file (its currently around 150MB and there's defintely room for improvement there).
I used the following command to list the contents of the JAR file that's produced:
jar tf <JAR file>
This revealed that there are lots of classes in the generated JAR file that are not used in the project. I believe these classes get included as part of third-party JARs.
Questions
(a) Is there an option that I can use to instruct sbt-assembly to generate a minimal JAR file that does not include the third-party classes that are not used in my project?
(b) I could use AssemblyStrategy to manually specify which files need to be excluded. Is this a sound strategy? I'm a bit concerned that with this approach the JAR file might end up throwing unexpected ClassNotFound exceptions.
Thanks in advance.
It's not easy to say what's used in your project and what is not. If you include a dependency into a project it might bring a few other ones in. Those child dependencies might also require their own dependencies and so on.
By default if you include some dependency in your project you intend to use it. The author of a dependency usually does the same thing. Thus, there is usually not much you can throw away, it's there for a reason. There are couple cases when this is not true:
Dependency author includes additional dependencies that will be used only in some settings, and that does not apply to your project
You are using a mega-dependency when you actually need only one of its libraries/features.
There are counter examples to this as well: Scalatest does not ship pegdown for generating html test reports because you don't need it usually. But it might be needed if you try to use -h flag to generate html.
Imagine the case when you use Apache Tika for pdf parsing. It wraps PDFBox to do the parsing. You don't need a bloat of all other libraries in that case that parse MS documents. The best thing to do is not to exclude files manually via sbt exclude or sbt-assembly rules because there is a risk you get it wrong and get run time class loading exception. Instead you need to use the right dependency like PDFBox directly. Unfortunately this is a lot of manual work in many cases to figure out all dependencies that you need, so it's your choice: easy and fat JAR, or painful and lean.
There are two ways to exclude dependencies:
Exclude transitive dependencies with exclude. See the docs here.
Don't use the top level dependency and manually add its subdependencies as you need them.
Ok, one more less fun option: use provided and make sure libraries are copied to your target environment and are on classpath. If you have many jars using the same libraries this helps to share those.
You can visualize your dependency tree with this plugin: https://github.com/jrudolph/sbt-dependency-graph. It's very helpful when trying to figure out what you are using and what you can remove. There are some tools like tattletale and loosejar that people suggest but I haven't tried them. If anyone has experience with those please share.
What might want to look at are treeshakers
For Java there's the following (I have not tried/used it):
http://proguard.sourceforge.net/

Swapping out .properties files for different environments

I have a Play 2 application that uses some external Java libraries. Some of these (e.g. the Paypal merchant sdk) depend on having a properties file for configuration (e.g. sdk_config.properties) which I have put into the conf directory.
When deploying this application to a different environment using play dist is there a sensible way to swap out properties files for each environment? For the main configuration in application.conf this is straightforward e.g. start -Dconfig.resource=prod.conf, but I am not sure how to do something similar for the properties files.
The support for specifying config.resource (and a lot of other nice stuff) is built into the typesafe config lib that comes with play. Other third party libs that depend on properties files on the classpath may or may not accept something like that, so if you are lucky then there is a lib specific way to point it to a separate config file.
If they do not then the only option is to somehow provide a different file on the classpath for the different environments. This could basically be done in two ways:
A. To not ship the file with the artifact and instead provide it per environment and add that on the classpath when starting play.
B. To create different artifacts for the different configurations, this would require you to customize dist to create one artifact per distinct env.
I would go for A since it allows there to be one artifact that can be run anywhere, but it might add some annoyances to running the app in a dev env etc.

SBT plugin: add resources to resulting jar?

I'm writing an SBT plugin. In this plugin, I need to add files to the resources directory within the resulting jar.
I don't want to change anything from the source directory (otherwise the user/developer may not understand why files appear to directory he's responsible for), but only in the generated jar.
How can I do that, is there a folder in target that correspond to the resource directory?
It seems like this is done by the "package" command, so if I can override the package command I might be able to do what I want.
Seems like I need to add stuff to resourceManaged.

Can I read property file with jetty in development?

I am working on a lift project as a sub-project in a gigantic mvn project. I put the property files into:
src/main/resources/props/staging.props
src/main/resources/props/production.props
in the sub-project folder.
Then I run jetty with:
mvn jetty:run -Drun.mode=staging
I printed out the settings from net.liftweb.util.Props using:
println("file_name:" + Props.fileName)
println("mode_name:" + Props.modeName)
The output:
file_name:lift.props
mode_name:staging
The mode name is correct. However, the file name is totally wrong. net.liftweb.util.Props is still using the file name that had been hardcoded in the source. It seemed never reached my property file.
Am I missing something? Or it cannot work with jetty?
While duffymo is right and you can just use the underlying servlet facilities for getting resources, I would still use Lift's Properties abstraction. It already does the work for differentiating different run modes. It also allows you to have different properties per-user and/or per-machine, which can be useful, depending on your development team.
The default properties for a given mode should be put in /props/modeName.default.props, so your files should be renamed to:
src/main/resources/props/staging.default.props
src/main/resources/props/production.default.props
I would put the .properties file in your /WEB-INF/classes and use the servlet context's getResourceAsStream() to read it in.