How to load resource file in jenkins plugin? - maven

I am trying to load a resource file which is in src/main/resources folder as part of Jenkins plugin. It is always giving me FileNotFoundException. Can someone please explain how to make it work?
Exception message:
java.io.FileNotFoundException: file:/var/lib/jenkins/plugins/Report/WEB -INF/lib/Report.jar!/properties.txt (No such file or directory)

The question is asked long back but I just thought to share my answer in case it helps someone out there who faced this issue similar to me.
Follow these steps: It works in my case:
Place your file in the "resources" folder thats typically on the
path "src/main/resources". In IntelliJ IDE, mark the resources
directory as "resource root".
As the file(s) are placed inside resources, they are in a directory
thats on the build path so maven should be able to load this without
setting additional build path.
Let say the file name is "application-env.properties". Following code block should
pull the file from resource folder at jenkins plugin run time.
InputStream inputStream = null;
try{
String resourceName = "application-env.properties";
Properties props = new Properties();
ClassLoader cl = <NameOfThisClass>.class.getClassLoader();
try (InputStream stream = cl.getResourceAsStream(resourceName)) {
props.load(stream);
}
//read props or return the same to the caller
}
finally {
if (inputStream != null) {
inputStream.close();
}
}

Related

in Quarkus, can I merge files that have the same name in many dependencies, ie typesafe config files?

I have a file called reference.conf that exists in many dependencies and derives from typesafe config (https://github.com/lightbend/config)
I use Quarkus for my application.
When Quarkus builds the uber-jar it keeps only one of these files (the one from the last dependency that it parses).
How can I merge all this files to a single one?
Thanks to this commit in Quarkus https://github.com/quarkusio/quarkus/commit/b3d3788ae92542d5fb39d89488890e16d64cec90 "Introduce UberJarMergedResourceBuildItem and UberJarIgnoredResourceBuildItem",
that works from release https://github.com/quarkusio/quarkus/releases/tag/1.13.4.Final we can create an extension in Quarkus and use it to merge any resource we want to.
Many thanks to George Gastaldi for this.
To create the extension is quite easy, as I did also for first time for this feature.
c:\projects> mvn io.quarkus:quarkus-maven-plugin:1.13.4.Final:create-extension -N -DgroupId=myproject.quarkus -DextensionId=quarkus-files-extension -DwithoutTests
Done, the extension has been created with all the needed maven projects, configuration etc. Now, edit the file of the extension
c:\projects\quarkus-files-extension\deployment\src\main\java\myproject\quarkus\files\extension\deployment\QuarkusFilesExtensionProcessor.java
add the BuildStep: UberJarMergedResourceBuildItem
class QuarkusFilesExtensionProcessor {
private static final String path = "reference.conf";
#BuildStep
UberJarMergedResourceBuildItem feature() {
return new UberJarMergedResourceBuildItem(path);
}
}
Build the Quarkus extension maven project, ie
c:\projects\quarkus-files-extension> mvn clean install
Now add the extension to your project that needs the merging by either executing
c:\project\mybigproject> mvn quarkus:add-extension -Dextensions="myproject.quarkus:quarkus-files-extension"
It simply adds the dependency of the extension in your pom.xml
That's it, next time that you build the uber-jar in the project, it merges all the reference.conf files to a single one.
The only thing missing is a verbose message, which is not possible with the current version,
You can notice that it works, by either of course checking the reference.conf in the uber-jar
or the following message is now missing from the build log
[WARNING] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Dependencies with duplicate files detected. The dependencies [groupId1:artifact1::jar:1.0(compile), groupId2:artifact2::jar:1.0(compile)] contain duplicate files, e.g. reference.conf
They also offer a feature to ignore files with a certain name, with the BuildItem UberJarIgnoredResourceBuildItem. You create an extension exactly the same way.
The solution proposed by Andreas is fine for files that can be appended sequentially, however that's not true for XML or JSON files, for example.
For these cases, it's preferable to perform the merge in a #BuildStep method in an extension and produce a GeneratedResourceBuildItem instead.
Here is an example using XmlCombiner:
#BuildStep
void uberJarMergedResourceBuildItem(BuildProducer<GeneratedResourceBuildItem> generatedResourcesProducer,
PackageConfig packageConfig) {
if (packageConfig.isUberJar()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
XmlCombiner combiner = new XmlCombiner();
List<URL> resources = Collections
.list(getClass().getClassLoader().getResources("META-INF/wsdl.plugin.xml"));
for (URL resource : resources) {
try (InputStream is = resource.openStream()) {
combiner.combine(is);
}
}
combiner.buildDocument(baos);
} catch (ParserConfigurationException | SAXException | TransformerException | IOException e) {
e.printStackTrace();
}
generatedResourcesProducer.produce(new GeneratedResourceBuildItem("META-INF/wsdl.plugin.xml", baos.toByteArray()));
}
}
See a test in https://github.com/quarkusio/quarkus/pull/17199

Activate profile while submitting Apache storm topology of a maven project (Without recompiling)

I have to activate the profile while submitting the jar to storm like we do for a spring/boot project. like below
java -jar project-xyz.jar --spring.profiles.active=dev.
It is a maven project with multiple sub modules. we have the resources structured
Anything that is common to all the profiles will go in the root directory of resources, and anything that is specific to a profile (like DB connections) will go in that particular profile directory.
The root directory file will have the place holders for profile specific properties and will be replaced by the actual properties defined in profile directory.
Ex: ${mysql.host} will be resolved to localhost when local profile is active.
This final file will be placed in the classpath when we build the jar using
mvn clean install -P{profile}
And then the topology is submitted to storm like the following.
storm jar project-xyz.jar path.to.class.containing.main.method
The final properties file generated by maven will be read by a property reader, stored and served whenever requested.
private static final Map<String, Properties> properties = new HashMap<>();
public static String getProperty( String file, String key )
{
try {
if ( !properties.containsKey( file ) ) {
synchronized ( properties ) {
if ( !properties.containsKey( file ) ) {
loadProps( file );
}
}
}
return properties.get( file ).getProperty( key );
} catch ( IOException e ) {
throw new PropertyException( "There was a problem getting " + key + " from " + file + ".properties", e );
}
}
private static void loadProps( String file ) throws IOException
{
InputStream inputStream = PropertyFileHandler.class.getClassLoader().getResourceAsStream( file + ".properties" );
if ( inputStream == null ) {
throw new FileNotFoundException( "Property file " + file + ".properties not found" );
}
Properties prop = new Properties();
prop.load( inputStream );
properties.put( file, prop );
}
I've already gone through this question and its different in a way that I use maven and I have to activate the profile instead of providing specific property.
So, is there a way I can activate the profile while submitting the jar to storm somewhat like the following?
storm jar project-xyz.jar --profiles.active=dev
If not, what can I do to achieve this without re-compiling the source? Does converting it to a spring boot project help?
You won't be able to do this as long as you're using Maven to generate a jar containing a single properties file. That jar can only be used in the environment you generated it for.
Instead, you could include all the possible properties files in the jar, and decide which one to read based on a system or environment property.
A better option is probably that you generate the property files separately to the jar, and then put the jar plus property file into Storm. You can pass the property file path to your Storm submission code as a command line argument to your main method. You would then read the properties into memory, and pass them to your bolts/spouts. You need to do this loading eagerly (i.e. read the entire properties at topology submission time, rather than when you need a specific property), so your lazy loading configuration code should be discarded.
You might want something like Flux for convenience:
To enable property filtering, use the --filter command line option and
specify a .properties file. For example, if you invoked flux like so:
storm jar myTopology-0.1.0-SNAPSHOT.jar org.apache.storm.flux.Flux
--local my_config.yaml --filter dev.properties
With the following dev.properties file:
kafka.zookeeper.hosts: localhost:2181
You would then be able to reference those properties by key in your
.yaml file using ${} syntax:
- id: "zkHosts"
className: "org.apache.storm.kafka.ZkHosts"
constructorArgs:
- "${kafka.zookeeper.hosts}"
See the documentation at https://storm.apache.org/releases/2.1.0/flux.html.

How to set file location in application properties - Spring boot

I have a Spring Boot application, the code need to access a file under resources/templates folder.
here is my application.properties file:
pont.email.template.location=templates/mailTemplate.html
This is the java file where I use the variable:
#Value("${pont.email.template.location}")
private String templateLocation;
----------------
BufferedReader reader = new BufferedReader(new FileReader(templateLocation));
The problem is not get the varibale, it returns correctly, The problem is that the application do not found any file for this path.
I always get
java.io.FileNotFoundException: templates/mailTemplate.html (No such file or directory)
I have checked that the file is in the path..
what is wrong in my code?
Help please, thanks.
You cannot read a File from inside a JAR. This fails due to the fact that the File has to point to an actual file resource on the file system and not something inside a JAR.
Let Spring do the heavy lifting and use the Resource abstraction to hide the nasty internals. So instead of using a String use a Resource and prefix the value of the property with classpath: to make sure it is loaded from the classpath. Then use an InputStreamReader instead of FileReader to obtain the information you need.
#Value("${pont.email.template.location}")
private Resource templateLocation;
----------------
BufferedReader reader = new BufferedReader(new InputStreamReader(templateLocation.getInputStream()));
In your application.properties prefix with classpath:.
pont.email.template.location=classpath:templates/mailTemplate.html
Now it should work regardless of the environment you are running in.

Classpath resource not resolving in spring boot

I am using spring boot and I have a file in resources folder. I am using digital ocean machine and when i run the application using java -jar mywebapp.war, I am unable to access the file from classpath. I am accessing it using following standard syntax:
File file = new ClassPathResource("mfile").getFile();
I am getting error that class path resource cannot be resolved to absolute path. The problem I see is that it is showing the path with ! marks as follows:
/home/u/webapp/target/mywebapp.war!/WEB-INF/classess!/mfile
What am I doing wrong here?
Since you're running it with java -jar you should build it as a JAR file instead of WAR.
Read more: https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html
Get file does not work while running as jar.you should get it as a resource Stream.
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("/file.xsd") ;
File file = File.createTempFile("file", ".xsd");
try {
FileUtils.copyInputStreamToFile(inputStream, file);
} finally {
IOUtils.closeQuietly(inputStream);
it gets you a file. if the requirement is to get as a file.

Junit file path

I am passing a filePath to JUnit as follows
public static void copyFile() throws IOException
{
String inputFileName = "myproj/src/test/resources/list.csv";
File file = new File(inputFileName);
File newFile = new File(new File("/tmp"), file.getName());
FileUtils.copyFile(file, newFile);
}
The package Structure is as follows.
I am running Test.java from IDE (IntelliJ) and it runs fine but when I do mvn clean install i get the below exception
java.io.FileNotFoundException: Source 'myproj/src/test/resources/list.csv' does not exist
at org.apache.commons.io.FileUtils.copyFile(FileUtils.java:1004)
at org.apache.commons.io.FileUtils.copyFile(FileUtils.java:968)
at ......copyFile(Test.java:21)
Can some one please let me know how am i suppose to give path name in the JUnit.
The exception is a strong hint to the problem... the file does not exist at that path.
In your IDE the working directory is set such that your relative path (myproj/src/test/resources/list.csv) resolves correctly.
The Maven task is running from a different working directory, presumably from myproj, so the file path cannot be resolved.
You have a couple of possibilities:
Use an absolute path
Load the resource from the classpath instead, using getResourceAsStream https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)
Build different relative paths based on the environment (this is a bad solution)

Resources