Gradle change resource files target location in JAR - java

I am Gradle-fying a legacy project with the following structure:
root
+--- common
| \--- config
+--- module1
\--- module2
In the original project, config is just a folder that contains configuration files organized in subfolders for different environments. It contains a top level folder props and many subfolders as in:
config
\--- props
+--- prod
+--- dev
+--- john
\--- mike
The project can be configured to use any of the subfolders. The configuration gets loaded by a method that looks like this:
Config.class.getClassLoader().getResourceAsStream("props/" + ENV + "/" + name);
where ENV defines the environment (it's a system property), i.e., it can be prod, dev, mike, etc., and name is just the name of the file to load.
When running tests, I need to have the props folder in the classpath. When building the production artifacts (JARs and WARs) I wan't to avoid that and manually copy only the files I need in order to avoid possible conflicts or accidents.
So I decided to make config its own Gradle project and add it as a testCompile dependency to other modules that require configuration. However, if I add the props folder as a resource folder in Gradle, the generate JAR file for the config module will flatten all the subfolders of props (which is the intended behavior), and thus the code above will simply fail.
My question is: is there a way to tell Gradle to copy those files to a subfolder called props instead of to the root of the JAR?
I know it would be easy to refactor the project and move the folders around but we are in a phase of transition from legacy build and deployment tools and want to maintain the original structure as much as possible until we can switch to Gradle completely. It's an iterative process and can't happen overnight. So I need an interim solution.

Here's how I ended up doing it. This is the build.gradle file for the config module:
apply plugin: 'java'
sourceSets {
main {
resources {
srcDir 'props'
}
}
}
// this is to force Gradle to create the JAR used at
// runtime with the correct folder structure
jar.into('props')
idea.module.iml.withXml {
def node = it.asNode()
// this is to force IntelliJ to create the folders
// used at runtime with the correct folder ('package') structure
node.component.content.sourceFolder.#packagePrefix="props"
}
It works like a charm because the config module only contains resources in the props folder. Whew.

Related

Files cannot be found when relative to property file in Java/Gradle

My Java project uses Gradle. I'd like to include a configuration folder conf when distributing the application with the task distZip.
This is an excerpt from my current project structure.
src
|-- dist
|-- conf
|-- application.properties
|-- keystore.jks
|-- truststore.jks
The conf folder is successfully distributed.
In my main() I load the properties with new File("src/dist/conf/application.properties") which works fine.
The application.properties contains two properties:
keystore.location = ./keystore.jks
truststore.location = ./truststore.jks
When starting the application from IntelliJ it works finde but when starting the distribution it cannot find the keystore.jks and truststore.jks because src/dist/conf does not exist but only conf.
How can I make sure that these files are found?
Your src/dist/conf folder is no resource folder for the java gradle plugin (and IntelliJ). It is just picked up by the application plugin (distZip task). IntelliJ sets the execution directory to the root path of the project. So it is just a coincidence that the src/dist/conf/application.properties can be read.
Move the files in from conf/dist folder to src/main/resources and they will be found by the processResources task and this folder should also be recognized by IntelliJ as a resource folder. Your classpath will have the resource node as root, so that you can use an absolute classpath to get to your files. (straight-forward sample: new File(YourClass.class.getResource("/dist/application.properties").getFile()))
I think that it will take much more effort to read the files in the conf/dist folders, because they are not part of the classpath.

How do I build an OpenFire plugin using Gradle in Intellij?

According to the OpenFire documentation (https://www.igniterealtime.org/builds/openfire/docs/latest/documentation/plugin-dev-guide.html) to build a custom plugin I need to create a jar with the following folder structure:
myplugin/
|- plugin.xml <- Plugin definition file
|- readme.html <- Optional readme file for plugin, which will be displayed to end users
|- changelog.html <- Optional changelog file for plugin, which will be displayed to end users
|- logo_small.gif <- Optional small (16x16) icon associated with the plugin (can also be a .png file)
|- logo_large.gif <- Optional large (32x32) icon associated with the plugin (can also be a .png file)
|- classes/ <- Resources your plugin needs (i.e., a properties file)
|- database/ <- Optional database schema files that your plugin needs
|- i18n/ <- Optional i18n files to allow for internationalization of plugins.
|- lib/ <- Libraries (JAR files) your plugin needs
|- web <- Resources for Admin Console integration, if any
|- WEB-INF/
|- web.xml <- Generated web.xml containing compiled JSP entries
|- web-custom.xml <- Optional user-defined web.xml for custom servlets
|- images/
I know there is an Ant build script to help do this but I couldn't find it and I'm having a hard enough time with Gradle and Maven, I'd rather not add having to learn Ant and deal with XML on to my list of chores. So, I tried to make a Gradle build script. Unfortunately Gradle still doesn't make any sense to me and in Intellij it seems to just do whatever it wants.
Regardless, this is the Gradle script I came up with.
task buildPluginJar {
group 'build'
description 'Builds OpenFire Plugin Jar'
println 'Clean old libs and classes.'
delete 'pluginDefinition/lib/*'
delete 'pluginDefinition/classes/*'
println 'Copy libs.'
copy {
into 'pluginDefinition/lib'
from configurations.runtime
}
println 'Copy classes.'
copy {
into 'pluginDefinition/classes'
from 'build/classes'
}
println 'Build jar.'
String outputPath = 'build/out/' + project.name + '.jar';
jar {
into outputPath
from fileTree('pluginDefinition/').include('**/*').collect { zipTree it }
}
}
I managed to get the file copying to work, but the end where it's supposed to put it all into a signal jar isn't working. It completes but there is no jar output. What am I doing wrong?
I could not figure out how to do this with Gradle. But I did figure out how to build jars with Intellij. I followed this tutorial:
http://blog.jetbrains.com/idea/2010/08/quickly-create-jar-artifact/
I went through the File > Project Structure menu and added two artifacts. One to build the jar with all of the java code in my pluginDefinition/lib folder, and another to build a jar with the full contents of the pluginDefinition folder that I could install on my Openfire server.
Still, it would have been nice if I could have done this with Gradle.

Filepath in war

I created sigleton ApplicationConfig for settigs, that reads xml config file. But every time it fails on path to file.
hierarchy:
META-INF
src
--application
----config.xml
--engine
----ApplicationConfig
web
--WEB-INF
----web.xml
I've tried File f = new File("../application/config.xml"); but it gives C:\WebLogic\application\config.xml
It's usually a bad idea to store your configs in 'src'. It's better to separate your code and configuration. I suggest you to read about maven (or gradle).
Basic maven app has pretty simple structure:
src
--main
----java
----resources
where in 'java' folder you store your code, and in the 'resource' folder you store your configs. And now you have problem, because jvm is trying to find your file relatively WebLogic base folder.
And if you use maven, you could just write:
ApplicationConfig.class.getResourceAsStream("/config.xml")
Of course, in this case you should put your config into 'resources' folder.

Customized ConsoleAppender in Eclipse RCP gives circular dependencies

I'm working on a Eclipse RCP application, and I would like to have a customized ConsoleAppender, so I can redirect all logs to the log window.
The log4j plugin and the log4j fragment (containing the log4j.properties) creates a unit which I manage to use successfully.
I've also created an 'extension' plugin containing my code to capture the log-data. Behold the 2 plugins and the fragment below.
rcp_external_log4j (plugin containing the jar file)
log4j-1.2.16.jar
rcp_external_log4j_fragment (containing the log4j.properties file)
log4j.properties (which points to VirtualConsol)
rcp_external_log4j_extension (plugin containing the VirtualConsol)
src/VirtualControl.java (which extends ConsoleAppender)
When I execute my project from within the Eclipse debug environment everything works fine. The VirtualConsole forwards all logging data as expected.
However, when I try to export the project with the 'Eclipse product export wizard' to a standalone executable I get the following problem:
Problem Occured
'Export Product' has encountered a problem.
A cycle was detected when generating the classpath
rcp_external_log4j_extension
rcp_external_log4j
rcp_external_log4j_extension
The VirtualConsole extends ConsoleAppender and is also called from other parts of the code.
The VirtualConsole is located in rcp_external_log4j_extension and uses log4j due to the extending of ConsoleAppender.
The rcp_external_log4j uses the rcp_external_log4j_extension, due to that the rcp_external_log4j plugin is bundled with the rcp_external_log4j_fragment, which refers to the VirtualConsole in the log4j.properties file.
Question: How can I write my own ConsoleAppender without getting a circular dependency? Can I put the code in the fragment? Can I put the code in the plugin containing the jar file (rcp_external_log4j)? I've tried those 2 attempts but without success...
Grateful for any help
Problem solved. src is put in the same plugin as the jar file.
rcp_external_log4j (plugin containing the jar file)
log4j-1.2.16.jar
src/VirtualControl.java (which extends ConsoleAppender)
rcp_external_log4j_fragment (containing the log4j.properties file)
log4j.properties (which points to VirtualConsol)
and 'src' added to the classpath of rcp_external_log4j. Seen in the MANIFEST.MF as
Bundle-ClassPath: log4j-1.2.16.jar,
src/
This way the VirtualConsole may be used both from the fragment and from the other code.
And the build.properties should contain:
jars.compile.order = src/
source.src/ = src/
output.src/ = bin/
for the code to be included in the exported product.
I don't think rcp_external_log4j_fragment needs to have rcp_external_log4j_extension in its build path, only in the manifest. This should remove the cycle.

Spring Application Context can't load the configuration files

I got 2 projects. A plugin project containing some components (POJOs) and a fragment project containing the according unit and integration tests. I'm using Tycho to build these projects and I want to use Spring to bootstrap my integration tests.
I've annotated my test classes with
#ContextConfiguration(locations = { "classpath*:spring/*-config.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
But when I try to build the projects with tycho (clean install) or run the test class as Plugin-Test within eclipse, Spring complains that there are no beans in the context defined. In the log I found the following lines:
DEBUG o.s.t.c.s.AbstractGenericContextLoader - Loading ApplicationContext for
locations [classpath*:spring/*-config.xml].
DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 0 bean definitions from
location pattern [classpath*:spring/*-config.xml]
I've put the configuration files under src/main/java/spring/ and src/main/resources/spring but spring can't find them. I've also tried to add these paths explicit to the bundle-classpath in the manifest.
When I change the configuration path to "file:spring/some-config.xml" spring is loading my bean definitions but crashes when it tries to load the "context" schema with the following output:
Configuration problem: Unable to locate Spring NamespaceHandler for XML schema
namespace [http://www.springframework.org/schema/context]
Why is it not working with the classpath prefix? And why is it working with the file prefix? I thought the file prefix would only work for the file system and not for a jar file... What am I doing wrong?
Thanks in advance
Update: Here is a complete view of the (fragment) test project:
/
+-- src/main/java/
| +-- MyTestClass.java
|
+-- src/main/resources/
| +-- spring/
| | +-- some-config.xml
| +-- log4j.properties
|
+-- META-INF/
| +-- MANIFEST.MF
|
+-- pom.xml
After tycho has tried to execute my test class I see the following files under target:
/target
|
+-- classes/
+-- MyTestClass.class
+-- spring/
+-- some-config.xml
+-- log4j.properties
+-- work/ // contains the eclipse configuration (config.ini, etc.)
+-- MANIFEST.MF
+-- mybundle-xx.jar
I've ommitted the properties and surfire files. The generated config.ini under target/work/configuration/ lists all bundles that are mentioned in the manifest as required bundles. They are referenced as jar files except of my test fragment bundle. For the test bundle the following entry exists:
reference\:file\:C\:/[...]/workspaces/workspace/my.bundle.tests
Is this correct? It would at least explain why the file prefix is working...
But what about the classpath prefix? Has the manifest been copied to the right location in the target folder? I mean it's outside of the classes folder that is referenced in the dev.properties.
Furthermore log4j complains at startup that it's not properly configured which indicates that it can't find the log4j.properties on the classpath.
Update: Now I'm trying another way. I've read this article and it seemed to be an easier way to get things running. So I've added the maven-surfire-plugin to my pom and changed my packaging type from "eclipse-test-plugin" to "jar" so that tycho doesn't run it's own surefire-plugin. But now I'm standing in front of another problem. Spring seems to provide only an ArtifactLocator for maven2 repositories and not for p2 repositories like tycho uses.
Does anyone know if there is an ArtifactLocator for p2 repositories out there?
Is anyone using the same setup with tycho, osgi and spring for integration testing?
Put spring-context-xx.jar on your classpath.
Namespaces are handled by implementations of the NamespaceHandler interface. At startup spring loads all of them, and attempts to parse each namespace with the loaded handlers. If none of them claims to be able to parse it, the exception is thrown. the context: namespace is parsed by ContextNamespaceHandler, which resides in the aforementioned jar.
Using Tycho,
according to http://blog.vogella.com/2010/07/06/reading-resources-from-plugin/
I tried locations like:
"platform:/plugin/<host-bundle-id>/<path-to-resource>"
and now its able to load the context configurations as a resource.

Categories

Resources