With the spring boot Gradle plugin, is there a task for writing the manifest file without packaging it - spring-boot

I am writing a plug-in using legacy code (on which I don't have any writing rights) for a spring-boot project, and I need to have access to the manifest file in his final form, as produced by the bootJar task. Sadly, this file seems too be directly written to the jar file, without any intermediate file.
Do you know a way to generate the Manifest file in its final form without unzipping the final jar ?

Not sure it helps, and couldn't write it as a comment. I had the same challenge with git, to log the commit id.
build.gradle:
plugins {id 'com.gorylenko.gradle-git-properties'}
gitProperties
project structure:
build
-classes
-generated
-libs
--x.jar
---com
---META-INF
----MANIFEST.MF
---git.properties
src
...
java:
Properties props = new Properties();
InputStream is = getClass().getClassLoader().getResourceAsStream("git.properties");
props.load(is);
log.info("git props: " + props);
so maybe you can try ..getResourceAsStream("META-INF/MANIFEST.MF");
Now I understood you need the access not from the app, but from the plug-in, but still..
Please let us know if you solved the challenge.

Related

Add a file to a specific folder with gradle build

I am currently working with a project (using gradle) that needs a properties file in the same package to the class that consumes it (I can't change this config). I have added the file in my package, but when I execute the gradle build command omits the file, and only adds the .class file. The app is deployed as a war file.
I have tried editing the war task, but I can't find the way to make the properties file to be added:
war {
from('src/main/com/foo/bar') {
include 'b.properties'
into 'WEB-INF/classes/com/foo/bar'
}
}
The war task does execute (I have added a prinln and it executes). Also, I have tried changing the paths, but no results. Also replacing from('path') to from (file('path')), but still doesn't seem to add the file anywhere.
How should I achieve this? Thanks!
Is there anything stopping you from following Gradle's convention for resources?
Eg put your property file at src/main/resources/com/foo/bar/b.properties
If you do this you won't need any task customisations

Make a simple JAR in gradle

I am new to gradle builds. I wrote a custom service for cloudera manager which needs to build a JAR file with few directories. It is a simple archive file with few directories(descriptor, images and scripts). I created it with below jar command manually.
jar -cf CSDNAME.jar descriptor images scripts
Now I want to include this as part of gradle build for which I need to write a task. I searched online where I found java related stuff which is not required in my case. Can someone help with this?
That's a code snippet using the kotlin dsl. It's based on the JAR task of the java plugin.
plugins {
java
}
tasks.jar {
doFirst {
archiveBaseName.set("CSDNAME") // By default the JAR will have the project name
from("content") // source folder where you have your content
}
}
N.B: If you already have a build file, you will need to change its extension to .kts, else you'll need of course to create one.

Spring Tools Suite and Gradle - Setup to use correct resources from inside STS

I have a Spring Boot Gradle project setup in Spring Tools Suite (3.7.2 RELEASE) with the following source folders:
- src/integration-test/java
- src/integration-test/resources
- src/main/java
- src/main/resources
- src/test/java
- src/test/resources`
Whenever I run the application or unit tests from within STS, I see that STS is using the resources found under src/integration-test/resources.
I see a duplicate resource warning in STS for files which exist in all 3 resource source folders. For example, I have an application.properties in all 3 source folders and I see following:
The resource is a duplicate of src/integration-test/resources/application.properties and was not copied to the output folder
If I run the application as a JAR or unit tests/integration tests from the command line (via gradle build), everything seems to use the correct resources. This makes me believe it is a problem with how STS/Eclipse is handling gradle.
Does anybody know of how I can configure STS to use the correct resource source folders when using gradle?
I think my problem may be related to (or the same as?) Spring Boot incorrectly loads test configuration when running from eclipse+gradle, https://issuetracker.springsource.com/browse/STS-3882, https://issues.gradle.org/browse/GRADLE-1777
I also tried the solution found here, but that seems to only fix Maven builds:
Spring Tool Suite finds spring-boot integration test configuration and does not start main application
I think my problem may be related to...
Yes, it is related but in my opinion not the same. That problem is caused by the runtime classpath being incorrect. This problem is an error coming from the eclipse project builder so it is a compile-time issue.
The problems are closely related though. Depending on your point of view, you could say they are the same (incorrect mixing of test and compile-time classpaths).
Here, specifically, the problem is that the eclipse builder tries to copy all the resources it finds in source folders to the project's single output folder. Each source folder has a 'application.properties'. The builder warns that it could not copy some of them because one would overwrite the other.
I think there may be a solution for this problem. But it is a solution that really should come from Gradle + ( BuildShip | STS Gradle Tooling) than from you.
It is possible in Eclipse to configure each source-folder individually to target a specific outputfolder. Maven + M2E are doing this correcty, but Gradle + (BuildsShip | STS Gradle Tooling) combdos do not.
For example this is what maven puts into the eclipse .classpath file when it configures a test resources folder:
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
Notice how it explicitly sets the output folder for that entry (to something different from the project's default output folder).
You may be able to address the problem yourself by modifying the .classpath for a gradle project in a similar way. Either by doing it manually or from your build.gradle.
I'm not sure this is worth it however as you will then likely still get hit by the runtime classpath issue (since these folders will still be added to your runtime classpath, your runtime classpath will end-up with two appication.properties resources, one which will 'shadow' the other. See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=482315)
I would say, the right thing to do is add a comment to the issue I linked, and hope they fix it soon as there is only so much you can do yourself by hacking the build.gradle file to modify the .classpath (this can not solve the runtime classpath issue, but in order to solve the runtime classpath issue, they would have to configure source folders to target individual output folder similar to what m2e does).
I would add this as a comment to #Kris's answer but it's too long.
I have solved the runtime classpath issue by adding the code below to my build.gradle file. The code generates an Eclipse launch configuration for the Spring Boot application class and includes only the runtime classpath (i.e. no test JARs).
My project uses the Gradle 'eclipse' plugin to generate the Eclipse project files (which I then import into Eclipse). Running the eclipseClasspath Gradle target will generate the launch file in the project's root directory.
def mainClassName = "com.example.MyApplication"
task eclipseApplicationLaunch {
group "IDE"
description "Generate an Eclipse launch configuration file for the Spring Boot application class"
}
eclipseApplicationLaunch << {
def writer = new FileWriter("${mainClassName.substring(mainClassName.lastIndexOf(".")+1)}.launch")
def xml = new groovy.xml.MarkupBuilder(writer)
xml.doubleQuotes = true
xml.launchConfiguration(type: "org.eclipse.jdt.launching.localJavaApplication") {
listAttribute(key:"org.eclipse.debug.core.MAPPED_RESOURCE_PATHS") {
listEntry(value:"/${project.name}/src/main/java/${mainClassName.replace(".","/")}.java")
}
listAttribute(key:"org.eclipse.debug.core.MAPPED_RESOURCE_TYPES") {
listEntry(value:"1")
}
listAttribute(key:"org.eclipse.jdt.launching.CLASSPATH") {
listEntry(value:"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n<runtimeClasspathEntry containerPath=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/\" javaProject=\"${project.name}\" path=\"1\" type=\"4\"/>\r\n")
listEntry(value:"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n<runtimeClasspathEntry path=\"3\" projectName=\"${project.name}\" type=\"1\"/>\r\n")
configurations.runtime.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def filePath = artifact.file.canonicalPath.replace("\\","/")
listEntry(value:"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n<runtimeClasspathEntry externalArchive=\"${filePath}\" path=\"3\" type=\"2\"/>\r\n")
}
}
booleanAttribute(key:"org.eclipse.jdt.launching.DEFAULT_CLASSPATH", value:"false")
stringAttribute(key:"org.eclipse.jdt.launching.MAIN_TYPE", value:"${mainClassName}")
stringAttribute(key:"org.eclipse.jdt.launching.PROGRAM_ARGUMENTS", value:"--spring.profiles.active=local --spring.config.location=conf/")
stringAttribute(key:"org.eclipse.jdt.launching.PROJECT_ATTR", value:"${project.name}")
stringAttribute(key:"org.eclipse.jdt.launching.VM_ARGUMENTS", value:"-Djava.net.preferIPv4Stack=true")
}
writer.close()
}
eclipseClasspath.dependsOn eclipseApplicationLaunch
I haven't modified the Eclipse .classpath file as per Kris' suggestion. Instead, I have added #Profile("test") to my test application class and #ActiveProfiles("test") to my test classes.

Loading a file as a string content in a Spring service contained in a JAR built with Maven

I am using Spring with Maven and I would like to load a file contained in the maven folder src/main/resources inside a spring service annotated with #Service contained in a JAR file (used by another WAR file as a Maven dependency). This does not seem to work for many different reasons (read: for many different exceptions according to many different attempts).
I know that Maven, when building, puts the stuff in src/main/resources at the root of the JAR file. I've opened the JAR file and I've verified that.
I would prefer a File object (something I can use with utility libraries like Apache Commons IO), but also "something" else that's working is fine.
I would prefere going for stuff within the Java class annotated with #Service (i.e. no application context XML file, with stuff inside that I recall within the service etc.)
The file contains stuff that I eventually use as a List<String> where every item is a line in the original file. I need to keep this file as it is, I can not use a database or another solution different than a file.
This is not a .properties file and is not intended to be.
The following are the not working attempts (throwing NullPointerException, FileNotFoundException, IllegalArgumentException: URI is not hierarchical and so on...).
Among the attempts I have tried also adding/removing the prefix src/main/resources while trying to figure out what was wrong, but then I figured out that Maven puts all the stuff at the root of the JAR file as stated before.
#Value("classpath:src/main/resources/myFile.txt")
private Resource myFileResource;
private File myFile =
new File(this.getClass().getResource("/myFile.txt").toURI());
private File myFile =
new FileSystemResource("src/main/resources/myFile.txt").getFile();
private BufferedReader bufferedReaderMyFile =
new BufferedReader(
new InputStreamReader(getClass().getResourceAsStream("myFile.txt")));
What's wrong with what I am doing?
File inside jar file is no longer a File, so treat it as Resource and read it via Stream
InputStream inputStream = this.getClass().getResourceAsStream("/myFile.txt");
assumming you have actually packed that myFile.txt inside jar file and it is available at the root of JAR file

Resolve a dependency dynamically inside a gradle task

I am trying to build a gradle plugin, which does the following:
As part of one its tasks, it creates a new configuration
It adds a DefaultExternalModuleDependency to this configuration - more specifically, it constructs a dependency to the application server zip file (available on Nexus). This information can be overridden by the invoking project as well.
Tries to resolve this newly added dependency and then unpacks the file to a local folder
All of this was working well when I had the details hard coded in a build file, but it looks like adding dependencies as part of a task are not treated the same way as having that information available at the parsing time.
So my question is, how do I get the project to reload the configurations / dependencies?
The code looks like the following:
#TaskAction
void installAppserver() {
Dependency dependency = new DefaultExternalModuleDependency(group,name,version)
Configuration configuration = project.configurations.detachedConfiguration(dependency)
configuration.setTransitive(false)
configuration.files.each { file ->
if (file.isFile() && file.name.endsWith('.zip')) {
println 'Attempting to unzip: ' + file + ' into folder: ' + appServerFolder
new Copy().from(project.zipTree(file)).into(appServerFolder).execute()
}
}
}
The problem is that the actual artifacts are not getting resolved!
A task can't configure the build model (that's what plugins do). It's fine to create and resolve a detached configuration in a task. If this doesn't work, there is likely a problem with the task's code, or the dependency it tries to resolve. Note that dependencies can only be resolved if the correct repository(s) are defined.
Instead of new DetaultExternalModuleDependency() (which is an internal class), project.dependencies.create() should be used. Instead of new Copy().execute() (Task#execute must not be called from user code), project.copy should be used.

Resources