Gradle multi-project with shared logging config - gradle

Is there a standard way to share a logging config (for log4j or logback for example) across all sub projects in a gradle project layout?
What I do right now is put a copy of logback.xml (or log4j.properties) in src/main/resources in each sub-project but this results in a lot of unnecessary duplication of this config file

This can be easily overcome using multiple working sets in gradle.
Add a new folder in the root of the project, example "shared-resources" put our configs inside it, and simple add the following line to your build.gradle on the sub-project
sourceSets {
main {
resources {
srcDirs = ["src/main/resource", "../shared-resources"]
}
}
}
This should add both files to your jar file.
An example can be find in github

Create a shared util module containing your Log4j2 configuration in its src/main/resources directory.
Then import the util module into others.
dependencies {
compile project(":util");
}
I also use the util module for re-usable Java code, not just for once-off configuration of Log4j2.

Related

How do you add text files to the classpath in Gradle? [duplicate]

How do I add config files or any other resources into my jar using gradle?
My project structure:
src/main/java/com/perseus/.. --- Java packages (source files)
src/main/java/config/*.xml --- Spring config files
Expected jar structure:
com/perseus/.. --- Java packages (class files)
config/*.xml --- Spring config files
I came across this post searching how to add an extra directory for resources. I found a solution that may be useful to someone. Here is my final configuration to get that:
sourceSets {
main {
resources {
srcDirs "src/main/resources", "src/main/configs"
}
}
}
Move the config files from src/main/java to src/main/resources.
Thanks guys, I was migrating an existing project to Gradle and didn't like the idea of changing the project structure that much.
I have figured it out, thought this information could be useful to beginners.
Here is a sample task from my 'build.gradle':
version = '1.0.0'
jar {
baseName = 'analytics'
from('src/main/java') {
include 'config/**/*.xml'
}
manifest {
attributes 'Implementation-Title': 'Analytics Library', 'Implementation-Version': version
}
}
By default any files you add to src/main/resources will be included in the jar.
If you need to change that behavior for whatever reason, you can do so by configuring sourceSets.
This part of the documentation has all the details
I ran into the same problem. I had a PNG file in a Java package and it wasn't exported in the final JAR along with the sources, which caused the app to crash upon start (file not found).
None of the answers above solved my problem but I found the solution on the Gradle forums. I added the following to my build.gradle file :
sourceSets.main.resources.srcDirs = [ "src/" ]
sourceSets.main.resources.includes = [ "**/*.png" ]
It tells Gradle to look for resources in the src folder, and ask it to include only PNG files.
EDIT: Beware that if you're using Eclipse, this will break your run configurations and you'll get a main class not found error when trying to run your program. To fix that, the only solution I've found is to move the image(s) to another directory, res/ for example, and to set it as srcDirs instead of src/.
Be aware that the path under src/main/resources must match the package path of your .class files wishing to access the resource. See my answer here.
As I have answered here, for more granularity while configuring the resource directories it's also possible to use srcDir.
sourceSets {
main {
resources {
srcDir "src/main/resources"
srcDir "src/main"
include "configs/**/*.xml"
}
}
}
So, if you have src/main/java/config/*.xml jar structure will have configs/*.xml as asked.
This is for Kotlin DSL (build.gradle.kts).
Add the following code to your subproject or app build.gradle.kts file:
sourceSets {
main {
resources {
srcDirs("src/main/configs", "src/main/misc")
}
}
// OR another notation
// main.get().resources.srcDirs("src/main/configs", "src/main/misc")
}
As mentioned by other answers, files in src/main/resources/ are automatically added to JAR. The srcDirs() function in above code adds its given paths to that existing path so files in those directories will be included in the JAR as well. You can add as many entries as you want.
Note that after adding the above code and syncing your changes with the IDE, some IDEs like IntelliJ IDEA and Android Studio show a helpful icon for those directories to indicate they are resources root directories:

how do you set up log4j for a gradle project with modules?

how do you set up log4j for a gradle project with modules?
I have a project set up like the following:
project root
build.gradle
gradle.properties
settings.gradle
// this root project does:
// include CommonModule
// includeBuild <all composite modules within module folder>
---CommonModule
------build.gradle
------gradle.properties
------settings.gradle
------src/main/groovy/...<common-code>
------src/main/resources/log4j2.xml
Modules
---OtherModule-1
// this is a gradle composite module
// it also includes the common module
------build.gradle
------gradle.properties
------settings.gradle
------src/main/groovy/...module1-code
------src/main/resources/log4j2.xml
---OtherModule-2
// this is a gradle composite module
// it also includes the common module
------build.gradle
------gradle.properties
------settings.gradle
------src/main/groovy/...module2-code
------src/main/resources/log4j2.xml
As shown above, we have a common gradle module and a module folder that contains module related composite module, each which depends on the included CommonModule
(core common code goes in common, the composite modules each contain code that extends common stuff
My question is hopefully simple:
where do I configure my log4j module?
e.g. can I put it in the common include module ?
or does each composite module need to have their own log4j xml?
I went and asked this same question on the gradle forums https://discuss.gradle.org/t/how-do-you-set-up-log4j-for-a-gradle-project-with-modules/28262 and got an answer -
Common module doesn’t include a log4j config Individual library
modules don’t include a log4j config either Top-level application
module does include a log4j config, but doesn’t put it on the
classpath (I set aside a src/[configuration]/config directory just
for that)
Launcher for the application itself (whether from a release
build or from a JavaExec in the Gradle build) specifies where the
log4j config is loaded from, so that you can’t get accidentally
poisoned by other configs on the classpath
you can define the xml location by providing a -D flag in the gradle properties file e.g. -Dlog4j.configurationFile=
also -Dlog4j.debug might help to track down issues

Gradle include Query Files

I have query files in same location as my classes. But gradle is not copying query files into classes directory after compiling. I added following below, but it did not work. Can you help how to resolve this issue?
I can't refactor to move query files.
processResources {
from('src/main/java') {
include '**/*.properties'
}
}
The follow structure
src
service
service1
Service.java
Service.properties
I want that the output generated by gradle would be
classes
service
service1
Service.class
Service.properties
If your structure is src/service/service1/Service.properties it is quite obvious why nothing is copied if you copy from src/main/java, isn't it?
Besides that, it might be better to configure sourceSets { main { resources { ... } } } instead of configuring the processResources task.
Gradle and Maven both use the same sensible convention
src/main/java is for java files ONLY
src/main/resources is for resources (eg properties, xml, images etc)
I suggest you stick to these very sensible conventions. Both Gradle and Maven can be configured to allow resources in src/main/java but this should be avoided if at all possible.
Don't mess with a default!
If you really want resources in src/main/java then I suggest you do
processResources {
from('src/main/java') {
exclude '**/*.java'
}
}

How do I copy a file into my WAR based on profile?

I’m using Gradle 2.7 on Mac Yosemite. I have the following files:
src/main/environment/dev/context.xml
src/main/environment/qa/context.xml
src/main/environment/prod/context.xml
What I would like is if I run a build gradle -Pqa build, the appropriate context.xml file above is copied into my WAR (into the WEB-INF/classes directory is fine). How do I set this up with gradle?
There're many ways of solving the problem. You can configure sourceSets, or include or exclude particular resources when building war file. You can also have single context.xml and perform resource filtering with ReplaceTokens filter.
I've chosen sourceSets:
apply plugin: 'war'
ext.env = project.hasProperty('env') ? project.env : 'dev'
sourceSets {
main {
resources {
srcDir "src/main/environment/$env"
}
}
}
The trick is to include/process only the env being passed. If no env is passed dev will be picked for further processing. Have a look a the demo.
You would have to do that using the environment variable. Have the system properties in a file. Read them in the build.gradle and based on it include the context.xml into the war.

Gradle dependency destination on non-jar config file

I can create a dependency to something other than a jar file like this:
dependencies {
compile files("../other-project/config.txt")
}
The above works fine, except that config.txt ends up in the WEB-INF/lib folder of my war file. Instead I need it to be in WEB-INF/classes in the war file, and in src/main/resources for jettyRun.
How can I control where the dependency ends up? Or am I going about this the wrong way?
I can also solve this with a copy task, but this really is a dependency in that I don't need the file updated unless it changes. An unconditional copy would work, but I'd rather do this the right way.
The war task (as configured by the war plugin) puts dependencies into WEB-INF/lib, the web project's own code/resources into WEB-INF/classes, and web app content (which by default goes into src/main/webapp) into WEB-INF. Other content can be added by explicitly configuring the war task. For example:
war {
into("WEB-INF/classes") {
from "../other-project/config.txt"
}
}
One way to make this work with embedded Jetty (though maybe not the most convenient during development) is to use jettyRunWar instead of jettyRun. Another solution that comes to mind, particularly if the content to be added resides in its own directory, is to declare that directory as an additional resource directory of the web project (sourceSets.main.resources.srcDir "../other-project/someResourceDir"). This is in fact an alternative to configuring the war task. If the web project already has a dependency on the other project, you could instead configure an additional resource directory for that project.
Let's say you have configured a multi-project build with the following directory and file structure:
/combined-war
/main-project
/src
/webapp
/WEB-INF
web.xml
build.gradle
/other-project
/resources
/WEB-INF
/classes
config.txt
build.gradle
build.gradle
In order to allow jettyRun to combine the contents of the webapp directory from main-project with the contents of the resources directory in other-project you need to add a workaround to your build.gradle of main-project (I've adapted the one posted by the user siasia on gist).
Adding the same directory content to the war file is quite simple and is documented in the Gradle User Guide and and the DSL reference.
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty'
import org.gradle.api.plugins.jetty.internal.JettyPluginWebAppContext
def newResourceCollection(File... resources) {
def script = '''
import org.mortbay.resource.ResourceCollection
new ResourceCollection(resources)
'''
def shell = new GroovyShell(JettyPluginWebAppContext.class.classLoader)
shell.setProperty("resources", resources as String[])
return shell.evaluate(script)
}
jettyRun.doFirst {
jettyRun.webAppConfig = new JettyPluginWebAppContext()
jettyRun.webAppConfig.baseResource = newResourceCollection(
// list the folders that should be combined
file(webAppDirName),
file("${project(':other-project').projectDir}/resources")
)
}
war {
from("${project(':other-project').projectDir}/resources")
}
Whenever you execute gradle jettyRun a new ResourceCollection is created that combines the given directories. Per default Jetty locks (at least on Windows) all the files it's serving. So, in case you want to edit those files while Jetty is running take a look at the following solutions.
Update
Since other-project in this case is not another Gradle project the two tasks in build.gradle should look like that:
jettyRun.doFirst {
jettyRun.webAppConfig = new JettyPluginWebAppContext()
jettyRun.webAppConfig.baseResource = newResourceCollection(
file(webAppDirName),
file("$projectDir/../other-project/resources")
)
}
war {
from("$projectDir/../other-project/resources")
}
I'm not aware of any solution that adds only one file (e.g. config.txt). You'll always have to add a complete directory.
As I mentioned above, it's simple enough to do an unconditional copy that solves the problem. Again, not the question I originally asked. But here's my solution that works for both war and jettyRun tasks:
processResources.doFirst {
copy {
from '../other-project/config.txt'
into 'src/main/resources'
}
}

Resources