using Proguard in a Maven project with several modules - java

I can't make Proguard Maven plugin to obfuscate files in a web project. I tried some solutions on Stackoverflow - no luck.
My actual application is Vaadin-based packaged with Maven, but I created a sample demo project https://github.com/taskadapter/projectX for now to illustrate the problem.
My end goal is creating a web application WAR file with all maven modules (e.g. "util") and web app code (e.g. "SomeImportantHiddenClass") obfuscated.
the build fails with:
[proguard] Error: The input doesn't contain any classes. Did you
specify the proper '-injars' options?
I saw http://maksim.sorokin.dk/it/2010/08/23/obfuscating-several-jars-in-one-single-maven-build-with-proguard/, but this does not make sense to me.
if I run proguard plugin on every maven module in my project separately, then how will other modules in the same project find required classes if they are all "messed up"?
I assume I need to add proguard execution to either root pom.xml or my web module, which actually packages stuff into a WAR file (see the project on GitHub).
"injar" option allows adding Jars to the obfuscation process, but I expected the plugin to find my modules and obfuscate them without me hardcoding "injar" values like "../util/target/util.jar".
plus proguard documentation does not say anything about "injars" option, it only describes "injar" configuration parameter. neither of them works in this case.
I also saw Proguard is saying it can't find any classes , but I'm not sure moving all the classes from "web" maven module to a separate one is the right solution and not sure it'll work anyway. I'll keep trying...

Related

Steps to change IntelliJ GitHub project to download and build source code for dependencies

Using IntelliJ IDEA, it is often a straight-forward task to check out a project from GitHub and get it to build locally.
Once the base project is imported from GitHub, the IDE will download artifacts which will allow the main project to run. The dependencies can be examined by using File > Project Structure... > Libraries.
So the IDE downloads dependencies to allow running, but not to build from source. The IDE is able to decompile classes, but the IDE will not automatically download the source code for those dependency libraries so that the programmer can alter the code.
This question is about the steps required in the IDE and project configuration such that a selected dependency will be built locally, and thus allow the programmer to alter the code.
What I tried was to import the project on which the main project was dependent, as a separate project, then configure the main project to utilize the local project instead of the downloaded artifact. The first step, downloading the sub-project and getting it to compile, was completed successfully.
So I ended-up with two projects, the main one, and the one on which the main project depended. The task at hand, if this was the appropriate way to get this done, would be to open the main project and take some action to convince the main project to use the local build, rather than the downloaded runtime "jar".
First, I edited the POM.xml to comment out the dependency for the sub project. Checking File > Project Structure... > Libraries, I could see that it was gone, and the build now failed (as expected).
I went to File > Project Structure... > Libraries > + (plus sign) and poked around with New Module, and Import Module, but I was not able to convince the original project to use the locally available sub project. The result from various attempts was that code in the base project was not able to import from the sub project (unable to compile).
What are the specific steps to take in the IDE to get what was a runtime dependency "jar file" to instead to build locally, and use that instead?
Use a SNAPSHOT version for the dependency (you'll need to change this in both your project's pom.xml and the dependency's pom.xml, so if the current version is 2.0.0 change it to 2.0.0-SNAPSHOT)
Then you can edit the dependency's code and run mvn install in the dependency to provide the new version of the dependency to your project.
TLDR: there is no simple and straight-forward way of downloading a project's code and the code of its dependencies to rebuild it in one go as a complete chain.
Your project depends on several other artifacts. From the screenshot, we may assume that this is a maven project, at least we can be sure that there artifacts with compiled classes available for download, because this happends during build. You can view the compiled classes of your dependencies, because Intelli has the capability of decompressing jars and decompiling code, obviously, but the contents you are viewing is read-only.
Sidenote: Maven convention is to create 3 separate jars for each project. One with compiled classes, one with source files only and one with generated documentation. By default intellij may not download these, but you can force it (right-click on pom.xml -> maven -> Download sources and documentation). This will attach the actual source code instead of decompiled classes to your IDE, so it's much easier to understand the code - but still, there is no option to modify it - it's still read-only extract from some jar.
So what if you want to actually edit the source? You have 3 options, all with its own set of problems that need human intelligence to solve:
You extract the decompiled source from classes jar
You extract the attached source from sources jar
You check out git repository of the dependency
Now, beware of the downsides of each approach:
You can be sure that the decompiled source matches your project dependency 1:1. But decompiled code is not easy to read, missing comments, etc. Also, some projects may not ship their build scripts with the classes jar. Anything more complex than mvn clean install may turn out to be a blocker.
You can be reasonably sure the code matches your project dependency, but this actually is not a given. There is a chance of human error, causing the sources to actually not match the compiled classes (build from different revision or whatnot). Much depends on the quality of the project, the discipline put into the build process and care to avoid environment specific configuration that is not part of the source. The larger and older is the project, the less chances are you are able to recompile it successfully using only src jar.
A sane man's approach. You should have your build scripts, readmes, tutorials, etc. Except, of course, if we are talking some obscure company internal project with zero effort put in its maintenance. Surely, there are the same issues as before: not all projects are rebuilt easily on any environment. There may be steps upon steps required for your workstation to be configured as expected. Hopefully, self-respecting open-source java projects are easy to build, but again - not a given - not all project are open-source, not all are self-respecting.
Important note: When checking out the git repo of your dependency - you must also make sure that you are using correct revision. If the project is maintained with respect for git tags/branches naming convention - you are in luck. Not a given by any means.
All the above is enough to discourage any attempts to automatically decompose dependencies to compilable units by your IDE, and all the burden is put into you. So let's assume the best - our dependency is a simple, self-contained java application that is easily built using simple mvn clean install. You have it checked out in a separate project in your IDE. You identified correct git revision that matches version your project depends on.
Now let's apply your little change and test it. First thing you want to do is change pom.xml of your project to use a made up version of your dependency. It should be a -SNAPSHOT version for clarity and tidiness. You may of course build your modified dependency with real release version - but please be wary of how maven manages dependencies. If you install version 1.0 yourself - it stays in your local repo forever. You will forget about it, and will be using your fake 1.0 version when building all other dependent projects unless you manually locate and remove it from repo. So stick to 1.1-SNAPSHOT.
Now every time you need to apply a small fix to your dependency, execute mvn clean install in its repo, then make sure your actual project depends on the correct new SNAPSHOT version, execute your maven clean install and that's it.
Note that all this has very little to do with Intellij. You are not expected to modify any library paths, advanced project settings, or links to jars. Stick to modifying pom.xml and you are set.

Packaging to Maven project

currently we are using ANT script to build the project and running jUnit tests. Now, we decided to move to Maven.
We have two web projects, Core-Project and Sub-Project. Now here it gets complicate. Their project is as follow.
Now, If I want to add this Sub-Project to Core-Project then I will create a jar of Sub-Project with WebContent folder and put it into Core-Project. Now whenever I run Core-Project, we have one utility class which extract content of Sub-Project into Core-Project.
Final(expected) project structure should look like this.
How can I achieve this in Maven? I mean how do I create a jar which contain some files located in src->main->webapp.
It seems that you need to rethink your architecture a bit. This answer may go beyond the scope of your question, but it's important to treat Maven as 'convention over configuration'. It is possible to achieve your layout using a combination of maven packaging tools, but if you restructure and follow Maven conventions, it will make more sense to people outside your project and be less work to maintain.
Suggested:
config-project
sub-project
core-project
config-project can hold the configuration for all parts of your application. sub-project and core-project can depend on this project and use it at runtime. You should package this project as 'jar' or 'zip' depending on the resources you need to make available to other projects.
sub-project should only contain the binary code common to the non-web based part of your application. It should be packaged as a 'jar' project and not be packaged with the config-project dependencies.
core-project should be packaged as a 'war' project. and follow the directory structure as suggested here: Maven War Plugin
Keeping the separation between your configuration, your non-web code and your web code will take a little bit to get used to. There is an excellent archetype by Tomcat which generates a maven project structure composed of these parts. It is easy to generate and inspect: Maven Tomcat Archetype

What is the use of the jar without dependencies Maven created for me?

I am using Maven and my configuration (which is most likely the default) produces this:
That means my compiled code is 4% of the file. Largest inflation was caused by the GitHub API library - I am strongly considering that I'll just drop it.
But my question is about the small file, not the big one. Maven creates it for a reason right? Could I maybe somehow distribute it and have it work on clients' computers? Why does it exist and what useful can be done with that?
Given your question, your Maven project is most likely a jar project that uses the maven-assembly-plugin to generate an uber-jar. In your output, there are 2 different files that are the result of 2 completely different process.
autoclient-3.0.jar is what's called the main artifact. This is the primary result of the Maven build. It consists of the compiled classes in your project alone, packaged into a jar by the maven-jar-plugin. You have this file because your project has the jar packaging. In Maven, the jar packaging automatically bind goals to build phases of the default lifecycle: among others, it includes an invocation of the jar:jar goal, which creates this main JAR. But you have to realize that this JAR only contains your classes. If you try to run the code, it will probably fail because the dependencies won't be there. What's its purpose if you can't run it then? Well, its purpose is to serve as a library for other projects, not as executable code.
Take, for example, a utility library that you would like to create: this library is not intended to be ran directly, it's intended to be used as a dependency for another project which will be executable. With the notion of transitive dependencies, Maven will automatically include in the buildpath of the other project your library and all its transitive dependencies. As such, your library does not need to embed its dependencies directly: they will be resolved correctly during the build of the other project.
autoclient-3.0-jar-with-dependencies.jar is what's called an additional artifact. jar-with-dependencies is a classifier that is used to distinguish this artifact from the main one. It is the result of the execution of the maven-assembly-plugin with the predefined jar-with-dependencies descriptor file. This artifact consists of the compiled classes of your project and all the direct and transitive dependencies of your project. An uber-jar (or fat jar) is really that: it aggregates all the dependencies and your code inside one big jar. The advantage is that you don't need to distribute separately the dependencies, they are already included in the JAR. But do note that its purpose is not to serve as a library, it is to be used as executable code.
Note that for a single project, it could make sense to keep both JAR: one intended to be used as a library (the main JAR) and the other intended to be used as runnable JAR.

Building scala with SBT to make JAR and folder with dependencies

I have a project in Scala (a kind of test utility) which is currently used only in sbt run way. However for certain demo I want to prepare it in a form which does not require sbt or scala preinstalled (only JVM).
First I've tried to use sbt-assembly plugin but soon get lost fighting with duplicate entries. So now I'm curious whether I can simply compile it to:
single jar-file containing application itself;
and lib directory containing raw set of dependency jars.
I hope that in such case it would be easy to run with the help of Main-Class and Class-Path: ./lib/* fields in the manifest - am I wrong? If this is correct, how can I achieve this?
Update: at last I conquered (it seems so) the sbt-assembly approach, so now the question is not as urgent (though I'm still curious to extend my knowledge of using sbt).
When execute sbt-assembly, all depedencies, App and resources will package into a single jar file.
You can override config properties in runtime by:
java -cp conf/:myAppDemo.jar App.run.mainClass
put your config properties files in conf folder.
Sbt one jar plugin can resolve more dependency conflicts, then assembly plugin.
Also take a look on merge section of assembly plugin, that can help you to fix problems like log4j.xml duplication. If you have problems with two classes with the same classpath having different content, try to exclude some duplicated dependencies (library management)

How to separate subproject classpaths in Eclipse?

I know that there is a Git plugin for Eclipse ("Egit"), but I like to do Git stuff on the command line, and I like to code in Eclipse, so I want to keep them separate.
I cloned a Git repo (I don't think its important, but for good measure, it was https://github.com/spinscale/dropwizard-jobs.git). I then opened up Eclipse (Juno) and created a new Java project, and selected the root of the cloned repo as the base path to my project. Eclipse asked me if I wanted to associate the project with the Java facet, and I clicked 'OK'.
This project has a large dependency tree (if you like, check out its 4 POM files). I don't use Maven to build (I use Gradle) so I just ran a script that resolves the dependencies of these POMs into a directory, and then I created a lib directory in this Eclipse project and copied all the JARs into it. I then added all these JARs to the project's classpath.
I am now seeing 10 errors in the Problems view in Eclipse, and they're all similar errors:
The type ApplicationStartTestJob is already defined ApplicationStartTestJob.java /dropwizard-jobs/dropwizard-jobs-core/src/test/java/de/spinscale/dropwizard/jobs line 10 Java Problem
The type ApplicationStartTestJob is already defined ApplicationStartTestJob.java /dropwizard-jobs/dropwizard-jobs-guice/src/test/java/de/spinscale/dropwizard/jobs line 10 Java Problem
8 more all like this, etc.
Sure enough, when I expand the entire project, I see it has the following structure:
dropwizard-jobs/
dropwizard-jobs-core/
src/test/java/
de.spinscale.dropwizard.jobs
ApplicationStartTestJob.java
dropwizard-jobs-guice
src/test/java/
de.spinscale.dropwizard.jobs
ApplicationStartTestJob.java
dropwizard-jobs-spring
src/test/java/
de.spinscale.dropwizard.jobs
ApplicationStartTestJob.java
So it seems that the maintainers of this project like to rename their unit tests with the exact same package/class names, and for some reason, Eclipse sees them as all belonging inside the same package. To test this I renamed dropwizard-jobs-core/src/main/java/de.spinscale.dropwizard.jobs.ApplicationStartTest to something else, and did the same for dropwizard-jobs-guice/src/main/java/de.spinscale.dropwizard.jobs.ApplicationStartTest and sure enough, all the errors associated with ApplicationStartTest being already defined went away.
So my suspicion is confirmed: The intention of these subfolders (dropwizard-jobs-core, dropwizard-jobs-guice and dropwizard-jobs-spring) is that they are sub-projects with separate classpaths. Eclipse thinks all of these source folders are part of the same project, and so it is lumping all of their classes into the same classpath. Since each subproject uses the same unit test naming conventions (same package/class names for each subproject), Eclipse see multiple classes in the same package as having the same name.
OK, good! I figured out the problem. But what's the solution? Ideally I would be able to keep all of these inside the same project, but perhaps modify the .classpath file or do something similar that instruct Eclipse to keep the subprojects separated from a classpath perspective. Any ideas?
SImply download eclipse m2e plugin, then import the project(considering you have already checked-out at your workstation), and do spend sometime learning MAVEN commands. here you can find an pverview of maven parent project and modules. Maven parent pom vs modules pom
One possible solution would be to introduce maven, which allows to naturally define a parent project and sub-projects in a multi-module maven project.
You can actually test that configuration outside of Eclipse, and then use M2Eclipse in order to import parent and its dependencies, at the same time (as commented in this answer) in your Eclipse.
Actually, the M2Eclipse project itself has guice test project, which you can use as model for your own guive subproject, in the repo sonatype/m2eclipse-guice, with an adequate pom.xml.

Categories