Using Maven for modifying an archive - java

Please help me with evaluation whether maven could be used for a problem I am faced with. Hopefully you can give me some hints on maven or suggest using a different tool.
Problem:
We have a template archive containing resources. We need to create a new archive based on the template using custom files/data.
some files from the template have to be replaced with custom ones.
some files have to be processed and placeholders in these files have to be replaced with custom variables. (i.e ${filename} -> "my_file_name.txt")
The pom file should describe which files should be replaced and where custom files should be taken from. Also it should describe which files to be processed by place-holder processor and where to get the variables values.
It is possible to write a java program to do all these stuffs, but Java code must be changed again and again for a different set of custom files and new placeholders. Hopefully with help of maven (also custom plugins if required) this task could be a simple configuration task.
Example:
Template folder:
/src/main/resources/file1.xml
/src/main/resources/file2.xml
/META-INF/MANIFEST.MF
file2.xml:
<file>${file_name}</file>
Custom folder:
/src/main/resources/file1.xml
/src/main/resources2/file3.xml
Custom Property File:
file_name=my_file_name.xml
Result:
/src/main/resources/file1.xml
/src/main/resources/file2.xml
/src/main/resources2/file3.xml
/META-INF/MANIFEST.MF
file2.xml:
<file>my_file_name.xml</file>
Questions:
How to put the unmodified MANIFEST.MF into target folder.
I tried to use
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
<manifestFile>src/main/resources/template/META-INF/MANIFEST.MF</manifestFile>
</archive>
but it simply disables 'maven' part of the META-INF. The manifiest.mf was still generated by maven
How to implement placeholder/variable processor?
How to zip the resulting set of files into .zip file?
How to invoke a maven build from a java class?
Is there an alternative tool which might be more suitable for my task?
regards,
ilia

That's a lot of questions, but I'll try to answer:
Move the META-INF/MANIFEST.MF to /src/main/resources/
It is already implemented on http://maven.apache.org/plugins/maven-resources-plugin/ for replacing properties on resources files with ${propertyName}
Zips can be created with http://maven.apache.org/plugins/maven-assembly-plugin/
Don't do that, Maven is invoked from command line, IDE or continuous integration server.
On tool would be Gradle, or the older Ant for naming some of them.

Related

Does maven-checkstyle-plugin checks files only within the /src directory

I wonder if the maven-checkstyle-plugin only check files within the /src directories of a project or multi-module project.
I do have a maven project where I don't have /src directories but loads of *.yaml, *.wsdl, *.json and *.xsd files and do not know if they are gonna checked with this plugin as well.
Yes, you can activate more checks for more filetypes in more locations. However...
You are probably using the check goal of the maven-checkstyle-plugin. As you can see, the default resource includes is only set to **/*.properties. Change it to anything you like.
To answer your seconds question, where checkstyle will look for sources and resources, there's an option for this in the same page:
<sourceDirectories> List 2.13 Specifies the location of the source directories to be used for Checkstyle. Default value is ${project.compileSourceRoots}.
However, the standard checks included in checkstyle do not include a lot of checks for files other than .java or .properties files. You might want to take a look at other plugins, like the spotless-maven-plugin's prettier configuration.

Handling duplicate entries in shaded (uber/fat) jar file

I created a jar file, created from multiple jars (I used Gradle Shadow plugin in this case).
It turned out that the file contains duplicate files in /META-INF. Most of them are multiple license.txt from the original jar files, but some of them are Spring factories, which I'm interested in.
This behaviour seems common when using various fatjar-like tools with Maven or Gradle, and it looks like it is not prohibited by any means.
I've checked that java.lang.ClassLoader#getResources will return these entries as a list of the same URLs, eg.:
jar:file:/C:/temp/myJar.jar!/META-INF/spring.factories
jar:file:/C:/temp/myJar.jar!/META-INF/spring.factories
so once I get the URL, there's no way back to getting the contents of all duplicated files, programmatically.
Can Java process such duplicate files, somehow?
Or, maybe more general, is it a valid and supported situation, from specs' point of view, to have a jar with duplicate files inside? I've checked Jar File Specification and it didn't give me a clue, either.
In maven-shade-plugin this is done with an AppendingTransformer.
My interpretation of the gradle plugin documentation you are using is that this is done this way:
shadowJar {
append('META-INF/spring.factories')
}

Maven dependency plugin problems

I use maven-assembly plugin to create a zip file for a release in my target folder. This package with dynamic name includes a configuration file;
/target/dailyrelease-4234.zip/cd/lib/conf/wrapper.conf
Now I also use maven-dependency plugin's build-classpath goal to output the dependencies as a string.
I want to write this output string to the configuration file created by the assembly plugin
I have 2 problems:
1- How can I access this conf file in the dynamic named zip?
2- I want to add some extra .jar paths to that string created by maven dependency plugin, but it only copies the names from local repository. is there a way to modify this output, or show dependency plugin to use another folder to pick the jar names and not from local repository?
Or even better make the creating dependency names task as a part of assembly-plugin so I dont need to access and modify that zip anymore.
1 - It sounds like your mechanism for dynamically generating that number '4234' exists outside of Maven, and you're trying to figure out how to access that number from within Maven, correct?
If so, I suggest using the buildnumber-maven-plugin which generates a number which you can then access from within Maven via the ${buildNumber} property.
2 - I suggest switching your callout from dependency:build-classpath to dependency:list -DoutputFile=xyz.txt. The latter gives you a cleaner output of just groupId/artifactId/version which should be easy to edit.
Or better yet... Do the above, and simply use <phase> configuration to ensure dependency:list gets called before the assembly plugin runs (typically at the end of package phase), and ensure the resulting output file sits somewhere that the assembly plugin will pick it up.
Hope that helps.

Managing Data Dependecies of Java Classes that Load Data from the Classpath at Runtime

What is the simplest way to manage dependencies of Java classes to data files present in the classpath?
More specifically:
How should data dependencies be annotated? Perhaps using Java annotations (e.g., #Data)? Or rather some build entries in a build script or a properties file? Is there build tool that integrates and evaluates such information (Ant, Scons, ...)? Do you have examples?
Consider the following scenario:
A few lines of Ant create a Jar from my sources that includes everything found on the classpath. Then jarjar is used to remove all .class files that are not necessary to execute, say, class Foo. The problem is that all the data files that class Bar depends upon are still there in the Jar. The ideal deployment script, however, would recognize that the data files on which only class Bar depends can be removed while data files on which class Foo depends must be retained.
Any hints?
This is one of the many problems Maven has already solved with it's build, dependency, and resource management. Any maven project follows a standard directory layout which dictates where you should put your Data files: in the 'resources' directories. The conventional Maven directory structure is as follows...
/
/src/
/src/main/java/
/src/main/java/App.java
/src/main/resources/
/src/main/resources/my.prod.data.or.cfg.or.whatever
/src/test/java/
/src/test/java/AppTest.java
/src/test/resources/
/src/test/resources/my.test.data.or.cfg.or.whatever
/pom.xml
The benefit of this is that all files which are contained in the 'main' (prod) resources directories are available to your application at run-time from the Classpath. All of the 'test/resources' files are available to your code during build & unit test time but are NOT included in your final artifact.
I don't think a generic solution exists for the system you describe, however, I just had a stab at reading annotations on classes using ASM, since that is used by jarjar as well. It is not hard to read the annotation data that way (pass in a ClassVisitor to the accept() method on ClassReader, and do something useful on the visitAnnotation callback). This means you can either try and include your intended behavior to jarjar or you could add it as a custom step to your build process.
Can't you refactor your project so that you have submodules that each contain the relevant files for the project itself ; Bar class and Bar related files will be packaged in their bundle while Foo ones will packed into another?
Another possibility would be to use some package naming convention to be able to filter the files you want to see i your bundles.

Classpath including JAR within a JAR

Is it possible to specify a Java classpath that includes a JAR file contained within another JAR file?
If you're trying to create a single jar that contains your application and its required libraries, there are two ways (that I know of) to do that. The first is One-Jar, which uses a special classloader to allow the nesting of jars. The second is UberJar, (or Shade), which explodes the included libraries and puts all the classes in the top-level jar.
I should also mention that UberJar and Shade are plugins for Maven1 and Maven2 respectively. As mentioned below, you can also use the assembly plugin (which in reality is much more powerful, but much harder to properly configure).
You do NOT want to use those "explode JAR contents" solutions. They definitely make it harder to see stuff (since everything is exploded at the same level). Furthermore, there could be naming conflicts (should not happen if people use proper packages, but you cannot always control this).
The feature that you want is one of the top 25 Sun RFEs: RFE 4648386, which Sun, in their infinite wisdom, has designated as being of low priority. We can only hope that Sun wakes up...
In the meanwhile, the best solution that I have come across (which I wish that Sun would copy in the JDK) is to use the custom class loader JarClassLoader.
After some research I have found method that doesn't require maven or any 3rd party extension/program.
You can use "Class-Path" in your manifest file.
For example:
Create manifest file MANIFEST.MF
Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass
Compile all your classes and run jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar
c stands for create archive
f indicates that you want to specify file
v is for verbose input
m means that we will pass custom manifest file
Be sure that you included lib in jar package. You should be able to run jar in the normal way.
based on: http://www.ibm.com/developerworks/library/j-5things6/
all other information you need about the class-path do you find here
Use the zipgroupfileset tag (uses same attributes as a fileset tag); it will unzip all files in the directory and add to your new archive file.
More information: http://ant.apache.org/manual/Tasks/zip.html
This is a very useful way to get around the jar-in-a-jar problem -- I know because I have googled this exact StackOverflow question while trying to figure out what to do. If you want to package a jar or a folder of jars into your one built jar with Ant, then forget about all this classpath or third-party plugin stuff, all you gotta do is this (in Ant):
<jar destfile="your.jar" basedir="java/dir">
...
<zipgroupfileset dir="dir/of/jars" />
</jar>
If you are building with ant (I am using ant from eclipse), you can just add the extra jar files
by saying to ant to add them...
Not necessarily the best method if you have a project maintained by multiple people but it works for one person project and is easy.
for example my target that was building the .jar file was:
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
<attribute name="Author" value="ntg"/>
................................
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
I just added one line to make it:
<jar ....">
<zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
<manifest>
................................
</manifest>
</jar>
where
<property name="external-lib-dir"
value="C:\...\eclipseWorkspace\Filter\external\...\lib" />
was the dir with the external jars.
And that's it...
Not without writing your own class loader. You can add jars to the jar's classpath, but they must be co-located, not contained in the main jar.
You need to build a custom class-loader to do this or a third-party library that supports this. Your best bet is to extract the jar from the runtime and add them to the classpath (or have them already added to the classpath).
I use maven for my java builds which has a plugin called the maven assembly plugin.
It does what your asking, but like some of the other suggestions describe - essentially exploding all the dependent jars and recombining them into a single jar
If you have eclpise IDE, you just need to export your JAR and choose "Package Required libraries into generated JAR". eclipse will automatically add the required dependant JARs into the generated JAR as well as generated some eclipse custom class loader that load these JARs automatically.
I was about to advise to extract all the files at the same level, then to make a jar out of the result, since the package system should keep them neatly separated.
That would be the manual way, I suppose the tools indicated by Steve will do that nicely.
Winstone is pretty good http://blog.jayway.com/2008/11/28/executable-war-with-winstone-maven-plugin/. But not for complex sites. And that's a shame because all it takes is to include the plugin.
Well, there is a very easy way if you're using Eclipse.
Export your project as a "Runnable" Jar file (right-click project folder from within Eclipse, select "Export..."). When you configure the export settings, be sure to select "Extract required libraries into generated Jar." Keep in mind, select "Extract..." and not "Package required libraries...".
Additionally: You must select a run-configuration in your export settings. So, you could always create an empty main( ) in some class and use it for your run configuration.
Anyway, it isn't guaranteed to work 100% of the time - as you will notice a pop-up message telling you to make sure you check the licenses of the Jar files you're including and something about not copying signature files. However, I have been doing this for years and have never encountered a problem.
Extracting into an Uber-dir works for me as we s should all be using root:\java and have outlets code in packages with versioning. Ie ca.tecreations-1.0.0. Signing is okay because the jars are intact from their downloaded location. 3rd party signatures intact, extract to c:\java. There’s my project dir. run from launcher so java -cp c:\java Launcher
In case you are using Spring Boot, you may want to have a look at this documentation: The Executable Jar Format
Java does not provide any standard way to load nested jar files (that
is, jar files that are themselves contained within a jar). This can be
problematic if you need to distribute a self-contained application
that can be run from the command line without unpacking.
To solve this problem, many developers use “shaded” jars. A shaded jar
packages all classes, from all jars, into a single “uber jar”. The
problem with shaded jars is that it becomes hard to see which
libraries are actually in your application. It can also be problematic
if the same filename is used (but with different content) in multiple
jars. Spring Boot takes a different approach and lets you actually
nest jars directly.
The Spring documentation also lists some alternative single Jar solutions:
Apache Maven Shade Plugin
JDotSoft JarClassLoader
One-JAR
Shadow Plugin (Gradle)
I would advise to use one jar and many libraries in separate jars, not in a single jar. Use separate jar from jar libraries.
Suppose you have such a folder structure:
path/yourApp/yourApp.jar
path/yourApp/lib/lib1.jar
path/yourApp/lib/megalib1.jar
path/yourApp/lib/supermegalib1.jar
All you have to do, add in MANIFEST.MF each of used jar.
Manifest-Version: 1.0
Main-Class: com.company.MyProgram
Class-Path: ./lib/lib1.jar ./lib/megalib1.jar ./lib/supermegalib1.jar
From within the manifest, you grant usage to each library.
Single all in one jar file might be easier to share and distribute, but in fact this doesn't give significant advantages over distributing as an archive and unpack it in some folder where you want to deploy. This will not make your program easier to maintain, faster. It will not make significant hdd usage difference.

Categories