IntelliJ Idea with Gradle: Best Practices - java

I am developing a Java application using IntelliJ Idea 14.1.4.
If it would have been solely Java application, I would have known exactly how to structure the project in Idea:
A single Java project, containing several modules: One for each part of the application (JAR).There will be at most 4-5 JARs.
The dependencies between the modules are also known: Protocol does not depend on anything, everything else depends on Infrastructure, and so on.
Next, I would like to use Gradle scripting for managing the project. So my question is what is the best practice to structure the code in Idea?
Should I create a single Gradle Project and a Gradle module for each of the JARs?
Should I create a single Java project (or maybe empty project) and Gradle modules for each of the JARs?
Should I create a single Gradle project and each of the modules will be a Gradle's sub-project? Maybe it will be better to have an empty project and several Gradle modules because not all of the JARs are closely coupled?
Since I have never used Gradle before, I would appreciate any guidance for the best practices when combining both Gradle and Idea.
Thanks,
Guy

As long as possible, I would keep the code in one source repository. On the root, I would have an "empty" project not outputting anything. All your jar projects would be sub-projects (in Gradle terms). You include them via the settings.xml file located in the root project.
Each sub-project has its own build.gradle file. In those files, you can easily define the dependencies between your sub-projects, e.g.:
dependencies {
compile project(':subProject3')
}
For convenience, I often create a special export task to put all artefacts in one export/ folder on the root level so that you don't need to go through all those sub-folders to get your stuff.
task export(type: Copy) {
from project(':subProject1').jar
from project(':subProject2').jar
from project(':subProject3').war
into 'export/'
}
IntelliJ Ultimate 14 works fine with this approach. You can simply hit Make to compile everything. You might also want to configure your project settings to run gradle jar or gradle export during a make if you prefer.

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.

Export Maven project with subproject to jar-file

I'm writing a lot of plugins for minecraft bukkit server's and I've grown tired of copy+pasting the same utility classes in my projects all over again. I decided to just put them all in a separate project and add them to my plugins via maven. I'm using IntelliJ Ultimate.
I want to have a maven project that contains all my utitily classes called e.g. UtilityAPI. Then I want to be able to create another project, which will be my bukkit plugin. In this second project I want to be able to use all the code from the first one. Also, I'd like it very much, that if I choose to build a plugin jar, maven automatically takes into account the most recent code from my API-Project.
I started working on this and started reading about maven modules and how you can use them to forge different projects together. I initially thought, that this was just what I needed, and tried to just add
<modules>
<module>UtilityAPI</module>
</modules>
However this results in my bukkit plugin project being considered a parent project, and refuses to build in a jar, but just in a (at least for me) rather useless .pom file. I'm not sure how to proceed. Do I have to create a "parent" project for my bukkit plugin project which contains the api and the plugin project as modules? And if yes, how do I generate a .jar, and not a .pom?
The dream solution would be to have the UtilityAPI project, and being able to include it in any new plugins that I might write in the future. I'd also be a fan of having a simple way to create a jar with the newest sources of my plugin in it. Is this possible, and if yes, how?
In your Maven multi-module project your plugin would have to be another module (and not the parent, which has packaging type pom). This module would then have a dependency on the API module (dependencies between modules are possible).
However, multi-module projects are usually intended for projects which are tightly coupled. That does not appear to be the case for your scenario. It sounds like the plugins have (potentially) nothing in common except for the dependency on the API project. Maybe it would be better to have the API project as separate standalone Maven project and then deploy snapshot versions of it (or install them only to your local Maven repository) and use these in your plugin projects.

Include plain java projects as dependency at gradle project

I have two projects
one plain java project and one gradle spring project created from jhipster
I would like to use the java project alongside with the gradle one.
After search on the net i have found how to do it link1 but this works only if all my projects are gradles.
Also when i try to add module dependency from Project Structure does not work.
Project tree
workspace
-project1
--build.gradle
-project2
What is the right way of doin this?
Also why i cannot make changes from IDE's project structure and automatically update build.gradle and settings.gradle with the new dependencies?
Change the plain java project into a gradle project, change the two projects into a gradle multi-project.
It's not hard to do, the plain java project probably just needs an almost empty build.gradle file and the settings.gradle file just needs to include it.
An IDE cannot help you much in these tasks, you will have to do most of it manually.

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

Refactor classes into a separate project

I intend to extract several classes and packages from one Java project and place them into another (which will have a distributable jar). That much isn't too difficult, but of course with such a large refactoring there are consequences. Namely there are many classes in the original project that are subclasses of the classes I want to extract. What's the best method for approaching this sort of refactoring?
You can create separate projects and the main project will have dependencies for all these projects. So in your IDE you can navigate through source code easily.
When building your application, each dependency could be built into a jar and the main application will be bundled with all the dependents jars in its classpath.
Let take as example a web app using plugins, and plugins using common classes, utilities and so on stored in a project named common-plugins.
project/webapp: having dependency on plugin1, plugin2 and common-plugin
project/plugin1: having dependency on common-plugins
project/plugin2: having dependency on common-plugins
project/common-plugins: having no dependencies
When building your project, you could build the plugins and the common-plugins into jars, bundled with your web app
project/webapp.war/WEB-INF/lib/plugin1.jar
project/webapp.war/WEB-INF/lib/plugin2.jar
project/webapp.war/WEB-INF/lib/common-plugins.jar
This way in your IDE, I will take eclipse for instance, you will have a workspace having 4 projects with dependencies as described above. At build using maven, ant, ivy, or what you want, you will build the 3 projects that the webapp project depends on, then bundle the whole stuff.
So basically this is what I did:
Create a new project
Copy over the appropriate classes from the old project to a new package in the new project, reconfigure until everything builds
Test that project separately and build it in to a jar
add jar as a dependency
Delete the classes from the original project
Manually change all the imports from the old packages to the new packages
What I was really looking for was some way to automate or streamline step 6 to make sure I didn't break anything, but I'm not sure it exists beyond mass find/replace.

Categories