Categories
Applications Bugzilla for Java CSV Compiling Miscellaneous Projects RS Library RsBudget Templating

The End of Atlassian JIRA

Atlassian announced the end of their various licensed stand-alone products. This heavily affects several of my projects, especially my Open Source projects. That’s why I am preparing now the migration away from Atlassian products. Of course, I could stay with Atlassian using their Cloud offerings. I like their products as they address my needs like no other products on the market. However, there are some downsides if I’d stay with Atlassian:

  • A migration of all existing data is unavoidable and will cost time and effort. It is not clear whether the Cloud product configuration would match my needs.
  • My Open Source projects are already code-hosted by GitHub. That’s why GitHub is the natural migration target for them.
  • I want to have full control over my CI/CD pipelines. A cloud Bamboo solution will take away a lot of freedom and I am not sure whether the various secrets I require during build and develop will stay on my servers and only there.
  • A long-term availability of issues and documentation is not guaranteed if I shall be forced to abandon projects.

So, all issue trackers are migrated to GitHub by today. The JIRA server has been shutdown. Please refer to the respective GitHub repositories in case you need to report an issue or require support for any of my projects.

Bamboo will be migrated to Jenkins. However, I experience some performance issues when starting Jenkins. That’s why this migration will still take a while. However, this would not affect your activities when using any of the projects.

I deeply regret to take this decision and would have loved to stay with Atlassion JIRA and Bamboo.

Categories
Bugzilla for Java CSV Java RS Library

Several new Software Releases available

As mentioned in previous post, most of my projects have been touched lately. So here is a list of the latest releases that you might find useful to integrate:

Also, there is a new project: EventBroker – a MQTT-alike event broker to enable losely coupled microservices being synchronized. It is written in Java and runs as docker container. Check it out when you look for an easy way to signal events to REST services in your distributed environment. Fun Feature: a special subscription topic for timer events that will enable you to implement cron jobs inside microservices – triggered by a REST call.

Categories
CSV Eclipse Java RS Library

Eclipse RCP Common Feature launched

Good news for all Eclipse developers that want to use some of my projects in their own Eclipse/RCP projects. I bundled some modules and projects into a Luna Eclipse Feature Plug-In – called RCP Common Feature.

You will need the Update Site http://download.ralph-schuster.eu/rcp-updates/luna/releases/ to be added in your IDE and install the feature as you would do with every other Eclipse feature plug-in.

These are the modules and projects currently bundled:

Furthermore, there are three more plug-ins available specific to Eclipse/E4 UI and logging. The feature plug-in is released under LGPL V3 license (as all projects bundled in it).

 

Categories
Java RS Library

Using Pooled Data Connections with RS Libs Data Hibernate Library

The standard way of configuring a data source for RSLib’s Data Hibernate Library is to use the driver’s DataSource implementation as shown here:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<dbconfig>
   <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
 
   <datasource class="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource">
      <property name="url">jdbc:mysql://db-host:3306/db-name</property>
      <property name="user">db-user</property>
      <property name="password">db-password</property>
   </datasource>
</dbconfig>

However, this setup will result in creating a new connection each time you start a new transaction. This is most likely not what you want in a productive application. Instead you shall use a pooled DataSource, as the c3p0 project provides:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<dbconfig>
   <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
 
   <datasource class="com.mchange.v2.c3p0.ComboPooledDataSource">
      <property name="driverClass">com.mysql.jdbc.Driver</property>
      <property name="jdbcUrl">jdbc:mysql://db-host:3306/db-name?autoReconnect=true</property>
      <property name="user">db-user</property>
      <property name="password">db-password</property>
   </datasource>
</dbconfig>

I stumbled across this problem while implementing a project and receiving a

java.net.SocketException: No buffer space available (maximum connections reached?): connect

exception.

Update July 26nd: You will need the autoReconnect=true parameter with MySQL to avoid problems with connections in the pools not being used for quite a long time.

Update August 18th: This URL parameter doesn’t work correctly. The solution is to add a c3p0.properties file in your classpath:

1
2
3
4
5
6
7
8
9
10
11
c3p0.maxPoolSize=50
c3p0.minPoolSize=5
c3p0.timeout=0
c3p0.maxStatements=0
c3p0.maxStatementsPerConnection=0
c3p0.idleConnectionTestPeriod=300
c3p0.acquireIncrement=1
c3p0.validate=true
c3p0.preferredTestQuery=SELECT 1;
c3p0.testConnectionOnCheckin=false
c3p0.testConnectionOnCheckout=true

(Don’t try to set these properties via your hibernate.cfg file. Some of them cannot be set there!)

Categories
Java RS Library

RS Library V1.2.5 released

I released RS Library V1.2.5 which mainly contains fixes to issues found by static code analysis. A complete list of changes can be found here.

The Maven repositories shall be up-to-date by now. The Maven coordinates are:

   <dependency>
      <groupId>eu.ralph-schuster</groupId>
      <artifactId>baselib</artifactId>
      <version>1.2.5</version>
   </dependency>
 
   <dependency>
      <groupId>eu.ralph-schuster</groupId>
      <artifactId>data</artifactId>
      <version>1.2.5</version>
   </dependency>
 
   <dependency>
      <groupId>eu.ralph-schuster</groupId>
      <artifactId>data-file</artifactId>
      <version>1.2.5</version>
   </dependency>
 
   <dependency>
      <groupId>eu.ralph-schuster</groupId>
      <artifactId>data-hibernate</artifactId>
      <version>1.2.5</version>
   </dependency>
 
   <dependency>
      <groupId>eu.ralph-schuster</groupId>
      <artifactId>templating</artifactId>
      <version>1.2.5</version>
   </dependency>
Categories
Bugzilla for Java CSV Java RS Library

Multiple Releases

The last few days I released new versions of three of my projects.

All projects are available through Maven Central.

 

Categories
Java RS Library

RS Library 1.2.2 released

A new version of the RS Library was released. Version 1.2.2 fixes many bugs found by unit tests. The previous version just had 12 unit tests, the new version got 190 unit tests to ensure quality of the code. A complete list of changes can be found here.

The Maven repositories shall be up-to-date by now. The Maven coordinates are:

   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>baselib</artifactid>
      <version>1.2.2</version>
   </dependency>
 
   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>data</artifactid>
      <version>1.2.2</version>
   </dependency>
 
   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>data-file</artifactid>
      <version>1.2.2</version>
   </dependency>
 
   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>data-hibernate</artifactid>
      <version>1.2.2</version>
   </dependency>
 
   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>templating</artifactid>
      <version>1.2.2</version>
   </dependency>
Categories
Java RS Library

The Class.forName() problem

In earlier Java days, it was easy to get a Class object when you had its name only. You simply called:

1
2
3
4
5
   // Without parametrized
   Class c = Class.forName("java.lang.String");
 
   // With parameters
   Class<String> = (Class<String>)Class.forName("java.lang.String");

This worked as long as there have been only one class loader. Long time ago :). The world kept spinning and concepts of separated applications in a single Java engine appeared. And so did OSGI. OSGI specified its container in a way that you can have a class being loaded in multiple versions at the same time. Furthermore, an OSGI bundle (effectively a JAR) can describe what other bundles can see their classes and what not. OSGI containers implement these behaviour through different ClassLoader instances responsible for the respective bundles.

However, this introduces a problem on Class.forName(String) calls. The method uses the ClassLoader of the “caller”. That means that it will be restricted to the bundle’s definition. As long as you use this method within bundle A to find a class in bundle A, everything works fine. It will even find a class in bundle B when B exported correctly and A defined B as a pre-requisite.

However, modular code encapsulates common functions. That’s why it can happen that a Class.forName(String) call is implemented in bundle C and doesn’t know about bundle A at all. When bundle A now calls bundle C which in turn calls Class.forName(String) to load a class from bundle A, then it fails. The reason is that ClassLoader C is not allowed to access classes in bundle A.

Several solutions are possible. The OSGI solution for this problem is the friend. You declare bundle C as friend of bundle A and class loading will succeed. However, this works only as long as you don’t try to load a class from bundle B. Now B would be required to declare C as a friend. If B is a 3rd party bundle then this solution is out of reach.

Another solution is the iteration on all OSGI bundles and trying to find the class via their class loaders. However, this is error-prone. Unless you don’t know exactly what bundle contains your required class you might get in trouble. Several versions of the same class can be present (or just define a class with the same name). You would need some heuristics to identify the correct class.

There is a better and most-likely the best solution. Each thread in Java can have a Context Class Loader. This will allow us to resolve all the dependency problems in OSGI. We will define the method in bundle C as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	/**
	 * Load a class by first checking the thread's class loader and then the caller's class loader.
	 * @param className name of class to be loaded
	 * @return the class found
	 * @throws ClassNotFoundException when the class cannot be found
	 */
	public static Class<?> forName(String className) throws ClassNotFoundException {
		return forName(className, null);
	}
 
	/**
	 * Load a class by directly specifying a class loader.
	 * @param className name of class to be loaded
	 * @param classLoader the class loader to be used - if null the thread's class loader will be  used first
	 * @return the class found
	 * @throws ClassNotFoundException when the class cannot be found
	 */
	public static Class<?> forName(String className, ClassLoader classLoader) throws ClassNotFoundException {
		if (classLoader == null) try {
			// Check the thread's class loader
			classLoader = Thread.currentThread().getContextClassLoader();
			if (classLoader != null) {
				return Class.forName(className, true, classLoader);
			}
		} catch (ClassNotFoundException e) {
			// not found, use the class' loader
			classLoader = null;
		}
		if (classLoader != null) {
			return Class.forName(className, true, classLoader);
		}
		return Class.forName(className);
	}

The method will (if no specific class loader was given) first test the Context Class Loader (lines 21-24). Only if this fails or is (for whatever reasons) not available, then the original Class.forName(String) method will be asked (line 30).

As the Context Class Loader is defined in the beginning of the executing thread, it is usually the class loader of Bundle A which has access to all other dependent bundles. Therefore we will be able to load classes from bundle A and B with code written in bundle C.

1
2
3
4
5
6
7
8
   // Code in bundle C
   public Class<?> getClass(String name) {
      return BundleC.forName(name);
   }
 
   // Code in bundle A
   Class<?> c1 = BundleC.forName("bundleA.className");
   Class<?> c2 = BundleC.forName("bundleB.className");

I added the helper methods in my RS Base Classes Library class LangUtils where you can use it directly from (V1.2.2 – available very soon) in your OSGI projects.

Categories
Programming languages RS Library

The Importance of Unit Tests

I am not a close friend of unit tests. In fact, as a programmer I regard them as annoying. However, when I started to write unit tests for my RS Library classes, I discovered many bugs that I didn’t see before. Those bugs never occurred in production yet mainly because the code was used in a slightly different way and bypassed the buggy parts. Some of the bugs are very stupid so that I wondered how they could ever get into a release.

The experience shows me now that it is important to write unit tests for all classes and their non-trivial methods. This is the only way to write better code. The self-confidence that I have in my own code is not enough. So there will be a lot of unit testing effort within the next weeks. And the next RS library code is most-likely the best-tested code ever.

Nobody is perfect. 😉

Categories
Java RS Library

RS Library 1.2.1 released

A major milestone of RS Library version 1.2 is out. I already discovered a critical bug in 1.2.0. That’s why the latest version is 1.2.1 now. The most important changes are:

However, I already discovered a minor bug in V1.2.1: RSLIBS-37 describes an incorrect behaviour of SoftMapCache.containsValue(Object) and WeakMapCache.containsValue(Object) methods which will be fixed in next version. V1.2.1 does not use this methods anywhere, so you would need to work-around this function in case you require it.

I also added more documentation now so you will be able to understand and use the libraries. It can be found either through Javadocs or the appropriate module homepages:

The Maven coordinates are:

   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>baselib</artifactid>
      <version>1.2.1</version>
   </dependency>
 
   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>data</artifactid>
      <version>1.2.1</version>
   </dependency>
 
   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>data-file</artifactid>
      <version>1.2.1</version>
   </dependency>
 
   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>data-hibernate</artifactid>
      <version>1.2.1</version>
   </dependency>
 
   <dependency>
      <groupid>eu.ralph-schuster</groupid>
      <artifactid>templating</artifactid>
      <version>1.2.1</version>
   </dependency>