How do I version my API?

Almost every enterprise application needs integration with one or more external systems. Different systems talk with each other using a set of well-defined APIs. However, as the system grow, you might be adding/removing/modifying the existing functionality. In this case, you need to be careful so that you don’t break the other system that might be using your APIs.

In order to tell the outside world that something has changed, we release different versions of the API. But how do you manage the versioning? Are your versions meaningful to the users? Do they convey an important message?

Semantic Versioning

Personally, I feel that you should be using Semantic Versioning (SemVer in short) if possible. Semantic versioning has been developed based on the existing notions of API versioning and addresses many problems efficiently. Below are some important points that define Semantic Versioning.

  1. The version number follows the convention MAJOR.MINOR.PATCH (example 1.3.2)
  2. Do not add preceding zeros to the versions. For example, doing 01.03.10 is not correct. This could easily lead to confusion.
  3. During initial development, keep the MAJOR version as 0. For example 0.1.1
  4. Once the first version is released, make it as 1.0.0
  5. Keep all the numbers nonnegative.
  6. The PATCH version must be incremented only if you make backward compatible bug fixes.
  7. The MINOR version must be incremented if you make backward compatible changes. This might also include introducing new features.
  8. When you increment the MINOR version, reset the PATCH version to 0.
  9. The MAJOR version must be incremented when you make backward incompatible changes. Reset MINOR and PATCH version when you do so.
  10. You can have additional labels for pre-release and build metadata are as extensions to the MAJOR.MINOR.PATCH format.

How to add an extra source directory in Maven?

Problem

Sometimes, you might feel the need to add an “extra” source directory in Maven. This becomes tricky specifically when you have already added that extra directory in your favorite IDE. Because of this, you will not see the compiler errors until you build the project using Maven. The need to add the extra source directories becomes more prevalent when you are using some automatic code generation (like Spring query DSL) in your project.

Solution

For the sake of covering a wider range of audience and to make this post more helpful, I will begin by considering that the default Maven configuration (only src/main/java and src/test/java added in your IDE).

Consider the below project structure in which only src/main/java and src/test/java added in source directories in your IDE (eclipse).directory_structure

Please note that there is a directory named “extra” that is not added as a source directory. Now go ahead and add the directory named “extra” as a source directory in eclipse.

Step 1) Right click on your project> Properties > Java Build Path> Add Folder> select “extra”
add_extra_src

Step 2) Once you complete the above step, you should be able to see the recently added directory added as the source directory.
extra_added

Step 3) However, doing so, the compilation errors in eclipse will be kicked out, however, you won’t be able to build this project using Maven. If you try to do so, you’ll get the below error.

[code language=”plain”]
[INFO] 2 errors
[INFO] ————————————————————-
[INFO] ————————————————————————
[INFO] BUILD FAILURE
[INFO] ————————————————————————
[INFO] Total time: 2.321 s
[INFO] Finished at: 2016-10-02T20:14:55+05:30
[INFO] Final Memory: 14M/161M
[INFO] ————————————————————————
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.5.1:compile (default-compile) on project multisrc: Compilation failure: Compilation failure:
[ERROR] /D:/eclipse/Workspaces/luna-ws/websandbox/multisrc/src/main/java/org/websandbox/multisrc/App.java:[3,27] package some.other.packages does not exist
[ERROR] /D:/eclipse/Workspaces/luna-ws/websandbox/multisrc/src/main/java/org/websandbox/multisrc/App.java:[15,9] cannot find symbol
[ERROR] symbol: variable ExtraSourceFile
[ERROR] location: class org.websandbox.multisrc.App
[/code]

Step 4) Add the Maven plugin to include the extra source directory in your build.

[code language=”xml”]
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.12</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/extra</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
[/code]

Step 5) Build your project and everything should be fine now. Download this project example from GitHub.

Further reading

You might see the below error in your pom.xml once you add the plugin mentioned above.
m2e-error

Don’t worry, just click “Discover new m2e connectors” and follow the steps.
Read more about the plugin usage here.

GC overhead Limit exceeded while running sonar runner

The problem

A common problem for someone who has recently set up SonarQube for code analysis. Specifically, the GC overhead limit exceeds for the projects having a mammoth code base. Memory is just not enough and the process goes for a toss and ultimately, it results in OutOfMemoryError.

Below is the stack trace that you might observe.

[code language=”plain”]
14:55:55.433 DEBUG – Release semaphore on project : org.sonar.api.resources.Project@5a7b5cb8[id=1,key=myProj_web,qualifier=TRK], with key batch-myProj_web
14:55:55.711 DEBUG – To prevent a memory leak, the JDBC Driver [com.mysql.jdbc.Driver] has been forcibly deregistered

INFO: ————————————————————————
INFO: EXECUTION FAILURE
INFO: ————————————————————————
Total time: 12:48.979s
Final Memory: 33M/910M
INFO: ————————————————————————
ERROR: Error during Sonar runner execution
org.sonar.runner.impl.RunnerException: Unable to execute Sonar
at org.sonar.runner.impl.BatchLauncher$1.delegateExecution(BatchLauncher.java:91)
at org.sonar.runner.impl.BatchLauncher$1.run(BatchLauncher.java:75)
at java.security.AccessController.doPrivileged(Native Method)
at org.sonar.runner.impl.BatchLauncher.doExecute(BatchLauncher.java:69)
at org.sonar.runner.impl.BatchLauncher.execute(BatchLauncher.java:50)
at org.sonar.runner.api.EmbeddedRunner.doExecute(EmbeddedRunner.java:102)
at org.sonar.runner.api.Runner.execute(Runner.java:100)
at org.sonar.runner.Main.executeTask(Main.java:70)
at org.sonar.runner.Main.execute(Main.java:59)
at org.sonar.runner.Main.main(Main.java:53)
Caused by: org.sonar.api.utils.SonarException: Can not execute Findbugs
at org.sonar.plugins.findbugs.FindbugsExecutor.execute(FindbugsExecutor.java:154)
at org.sonar.plugins.findbugs.FindbugsSensor.analyse(FindbugsSensor.java:59)
at org.sonar.batch.phases.SensorsExecutor.executeSensor(SensorsExecutor.java:79)
at org.sonar.batch.phases.SensorsExecutor.execute(SensorsExecutor.java:70)
at org.sonar.batch.phases.PhaseExecutor.execute(PhaseExecutor.java:131)
at org.sonar.batch.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:178)
at org.sonar.api.platform.ComponentContainer.startComponents(ComponentContainer.java:92)
at org.sonar.api.platform.ComponentContainer.execute(ComponentContainer.java:77)
at org.sonar.batch.scan.ProjectScanContainer.scan(ProjectScanContainer.java:199)
at org.sonar.batch.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:194)
at org.sonar.batch.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:187)
at org.sonar.api.platform.ComponentContainer.startComponents(ComponentContainer.java:92)
at org.sonar.api.platform.ComponentContainer.execute(ComponentContainer.java:77)
at org.sonar.batch.scan.ScanTask.scan(ScanTask.java:56)
at org.sonar.batch.scan.ScanTask.execute(ScanTask.java:44)
at org.sonar.batch.bootstrap.TaskContainer.doAfterStart(TaskContainer.java:82)
at org.sonar.api.platform.ComponentContainer.startComponents(ComponentContainer.java:92)
at org.sonar.api.platform.ComponentContainer.execute(ComponentContainer.java:77)
at org.sonar.batch.bootstrap.BootstrapContainer.executeTask(BootstrapContainer.java:175)
at org.sonar.batch.bootstrap.BootstrapContainer.doAfterStart(BootstrapContainer.java:163)
at org.sonar.api.platform.ComponentContainer.startComponents(ComponentContainer.java:92)
at org.sonar.api.platform.ComponentContainer.execute(ComponentContainer.java:77)
at org.sonar.batch.bootstrapper.Batch.startBatch(Batch.java:92)
at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:74)
at org.sonar.runner.batch.IsolatedLauncher.execute(IsolatedLauncher.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.sonar.runner.impl.BatchLauncher$1.delegateExecution(BatchLauncher.java:87)
… 9 more
Caused by: java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:232)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at org.sonar.plugins.findbugs.FindbugsExecutor.execute(FindbugsExecutor.java:146)
… 38 more
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
at edu.umd.cs.findbugs.util.Strings.escapeXml(Strings.java:167)
at edu.umd.cs.findbugs.xml.XMLAttributeList.getQuotedAttributeValue(XMLAttributeList.java:132)
at edu.umd.cs.findbugs.xml.XMLAttributeList.toString(XMLAttributeList.java:111)
at edu.umd.cs.findbugs.xml.OutputStreamXMLOutput.openTag(OutputStreamXMLOutput.java:112)
at edu.umd.cs.findbugs.SourceLineAnnotation.writeXML(SourceLineAnnotation.java:887)
at edu.umd.cs.findbugs.ClassAnnotation.writeXML(ClassAnnotation.java:192)
at edu.umd.cs.findbugs.BugInstance.writeXML(BugInstance.java:2292)
at edu.umd.cs.findbugs.SortedBugCollection.writeXML(SortedBugCollection.java:576)
at edu.umd.cs.findbugs.SortedBugCollection.writeXML(SortedBugCollection.java:497)
at edu.umd.cs.findbugs.XMLBugReporter.finish(XMLBugReporter.java:46)
at edu.umd.cs.findbugs.DelegatingBugReporter.finish(DelegatingBugReporter.java:81)
at edu.umd.cs.findbugs.DelegatingBugReporter.finish(DelegatingBugReporter.java:81)
at edu.umd.cs.findbugs.DelegatingBugReporter.finish(DelegatingBugReporter.java:81)
at edu.umd.cs.findbugs.FindBugs2.analyzeApplication(FindBugs2.java:1256)
at edu.umd.cs.findbugs.FindBugs2.execute(FindBugs2.java:282)
at org.sonar.plugins.findbugs.FindbugsExecutor$FindbugsTask.call(FindbugsExecutor.java:201)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
[/code]

I had posted a question on StackOverFlow here but I couldn’t get any answers. I have observed this question was getting noticed and hence I decided to include the answer in this post.

The solution

Luckily, is possible to configure Sonar Qube through the “sonar.properties” file. This can be done per-project basis as the property file lies in the individual project directory. Modify the property file as below.

  1. Skip the package design analysis, sonar.skipPackageDesign=true
  2. Remove the entries for lib/test directory in your project’s properties file
  3. Give some more memory set SONAR_RUNNER_OPTS=-Xmx1536m -XX:MaxPermSize=512m
  4. Optionally you can disable design analysis by using sonar.skipDesign=true
  5. Restart SonarQube services and you should be good now 🙂

Furthre reading

For the curious souls, below is the meaning of each property that we are setting.

  1. sonar.skipPackageDesign: Enabling this makes SonarQube analyse all of you source files to make sure they belong to the correct directory structure as mentioned by the source files package. This can be easily identified by most of the IDEs and hence, this analysis is not required. However, there are some cases (like the complete source file is commented out, including the package declaration) that cannot be easily identified. So be a little careful while toggling this flag.
  2. Remove the entries for lib/test: If you do not want to analyze the jars and the tests.
  3. SONAR_RUNNER_OPTS=-Xmx1536m -XX:MaxPermSize=512m: Giving some more room to SonarQube so that it can dance freely.
  4. sonar.skipDesign: It relates to the design plugin, which only analyzes project dependencies and modules.

For more reading, have a look at the SonarQube documentation.

Please note that in newer versions of SonarQube, some of the properties might be deprecated.