Sunday, 3 December 2017

This week 14/2017 - Jollyday

Jollyday is a small library to get dates of holidays. 
When you open project documentation page, you can think that this project is dead. The sourceforge page wasn't updated for last 2 years. However when you check GitHub repository, it is still developed since 2010. When I write this blog the most fresh version for JDK8 is 0.5.2.

Jollyday handles fixed, related to day of month and religious holidays. To be more advanced, it handles additional rules to define moving free days related to holidays, ex when holiday is on Saturday, next Monday will be holiday as well.

Below there is piece US configuration file. The Jollyday jar file includes default configuration for 64 countries. Some of them include holidays changes in time in the past. Polish configuration doesn't.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
..
14
15
16
17
18
19
20
<tns:Configuration hierarchy="us" description="United States"
                   xmlns:tns="http://www.example.org/Holiday" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.example.org/Holiday/Holiday.xsd">
    <tns:Holidays>
        <tns:Fixed month="JANUARY" day="1" descriptionPropertiesKey="NEW_YEAR"/>
        <tns:Fixed month="JULY" day="4" validFrom="1776" descriptionPropertiesKey="INDEPENDENCE"/>
        <tns:Fixed month="NOVEMBER" day="11" validFrom="1938" descriptionPropertiesKey="VETERANS"/>
        <tns:Fixed month="DECEMBER" day="25" descriptionPropertiesKey="CHRISTMAS"/>
        <tns:Fixed month="MAY" day="30" validFrom="1869" validTo="1967" descriptionPropertiesKey="MEMORIAL"/>
        <tns:FixedWeekday which="LAST" weekday="MONDAY" month="MAY" validFrom="1968" descriptionPropertiesKey="MEMORIAL"/>
        <tns:FixedWeekday which="FIRST" weekday="MONDAY" month="SEPTEMBER" validFrom="1895" descriptionPropertiesKey="LABOUR_DAY"/>
        <tns:FixedWeekday which="FOURTH" weekday="THURSDAY" month="NOVEMBER" validFrom="1863" descriptionPropertiesKey="THANKSGIVING"/>
    </tns:Holidays>
....
    <tns:SubConfigurations hierarchy="la" description="Louisiana">
        <tns:Holidays>
            <tns:FixedWeekday which="THIRD" weekday="MONDAY" month="JANUARY" validFrom="1986" descriptionPropertiesKey="MARTIN_LUTHER_KING"/>
            <tns:ChristianHoliday type="GOOD_FRIDAY"/>
            <tns:ChristianHoliday type="MARDI_GRAS"/>
        </tns:Holidays>
    </tns:SubConfigurations>

The HolidayManager is main interface of Jollyday library. Below there is a piece of code which create HolidayManager instance for US using default configuration.

1
2
ManagerParameter params = ManagerParameters.create(Locale.US));
HolidayManager holidayManager = HolidayManager.getInstance(params);

If we'd like to get holidays for US we need to execute code as below.

1
holidayManager.getHolidays(2017)


If we'd like to get holidays for Louisiana in US we need to call code as below


1
holidayManager.getHolidays(2017, "la")

It is possible to create deeper hierarchy of calendar. You must only create sub-configuration for this node and call get Holidays with additional code - code of sub-configuration.

To use custom configuration, you need create ManagerParameters from URL where is configuration file available, ex


1
2
3
URL url = ClassLoader.getSystemResource("Holidays_pl.xml");
ManagerParameter params = ManagerParameters.create(url);
HolidayManager m = HolidayManager.getInstance(params);

Below I added a few example of most common usage of HolidayManager.

1
2
3
4
5
m.getHolidays(2017)
m.getHolidays(LocalDate.now(), LocalDate.now().plusYears(5))
m.isHoliday(LocalDate.now())
m.isHoliday(LocalDate.now(), "an")
m.isHoliday(LocalDate.now(), HolidayType.UNOFFICIAL_HOLIDAY)

For more demanding, it is possible to implement:
  • country specific HolidayManager
  • configuration data source
  • change implementation of parsers
Then you need create your own properties as below (default properties file)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
manager.impl=de.jollyday.impl.DefaultHolidayManager
# Holiday manager for Japan implements some specific Japanese holiday rule.
manager.impl.jp=de.jollyday.impl.JapaneseHolidayManager
# Implementation class for holiday configurations
configuration.datasource.impl=de.jollyday.datasource.impl.XmlFileDataSource

# Configure the parsers to be used for each individual configuration type
parser.impl.de.jollyday.config.Fixed=de.jollyday.parser.impl.FixedParser
parser.impl.de.jollyday.config.FixedWeekdayInMonth=de.jollyday.parser.impl.FixedWeekdayInMonthParser
parser.impl.de.jollyday.config.IslamicHoliday=de.jollyday.parser.impl.IslamicHolidayParser
parser.impl.de.jollyday.config.ChristianHoliday=de.jollyday.parser.impl.ChristianHolidayParser
parser.impl.de.jollyday.config.RelativeToFixed=de.jollyday.parser.impl.RelativeToFixedParser
parser.impl.de.jollyday.config.RelativeToWeekdayInMonth=de.jollyday.parser.impl.RelativeToWeekdayInMonthParser
parser.impl.de.jollyday.config.FixedWeekdayBetweenFixed=de.jollyday.parser.impl.FixedWeekdayBetweenFixedParser
parser.impl.de.jollyday.config.FixedWeekdayRelativeToFixed=de.jollyday.parser.impl.FixedWeekdayRelativeToFixedParser
parser.impl.de.jollyday.config.EthiopianOrthodoxHoliday=de.jollyday.parser.impl.EthiopianOrthodoxHolidayParser
parser.impl.de.jollyday.config.RelativeToEasterSunday=de.jollyday.parser.impl.RelativeToEasterSundayParser

and setting VM options:

1
-Dde.jollyday.config.urls=file:/some/path/new.properties,http://myserver/some/path/further.properties,jar:file:myLibrary.jar!/my.properties  

I didn't find out if data source or parsers can be country specific and how to create your own parser.

Resources:
  1. jollyday.sourceforge.net
  2. github repository


Monday, 7 August 2017

This week 13/2017 (Hibernate)

I will write about few details connected with hibernate which are not commonly known.
  1. *Any
  2. Lazy loading - not working in all cases as expected
  3. Eager loading -  list collections
  4. @MapsId
  5. FetchMode and graphs
  6. Default constructor
  7. Final attributes


Ad 1. 
It is rarely to find @Any or @ManyToAny relation in code. However it is useful when it is need to create relation with many class's types. Table referring to many types should have additional column which defines a related type. 

1
2
3
4
5
6
@AnyMetaDef(name= "Vehicle", metaType = "string", idType = "int",
    metaValues = {
            @MetaValue(value = "C", targetEntity = Car.class),
            @MetaValue(value = "M", targetEntity = Motor.class)
    }
)

In this case in additional column are archived C and M - value of meta. Vehicle which is a interface for this two classes Car and Motor is used as a type in related entity. Principle of operation is similar to hardly typed relations.

Ad 2.
Lazy loading for relations @*ToOne not always works as it could be expected. Everything is connected with assumption that it is needed to know if relation with current entity is empty or not. When the key of relation is situated in coupled table, it is required to call additional query to check if that relation exists or not. If there is a relation hibernate creates proxy if not, it leaves null value. If we assume that entity has always related object it is possible to set attribute optional to false. In this case hibernate always creates proxy.
Other solution for this problem is to change table for relation key or manipulate with LazyToOne annotation and doesn't create proxy, what is not required.

Ad 3.
It is not common but sometimes I meet in entities that to keep collection some people use List. It is not recommended because some queries can receive multiplied instance of one row, especially it is common when it is used eager fetch mode.
In case when one entity has more then one eager list collection, we get javax.persistence.PersistenceException during start-up.


Ad 4.
@MapsId is an annotation which makes relation key of as primary key. It looks better in db, especially when entity has embedded key.

Ad 5.
JPA 2.1 introduce new feature - entity graph. This feature allows to change featch mode in runtime. One what is required, it is needed to define some path of connections between entities and use this definition in Query as a hint. This feature was available before JPA 2.1 but it wasn't standardized, so each implementation resolved it in other way.
Bellow some example.  

1
2
3
4
5
6
@NamedEntityGraphs({
        @NamedEntityGraph(name = "graph.Index.countrySymbols",
                attributeNodes = {@NamedAttributeNode(value = "country"), @NamedAttributeNode(value = "symbols")}),
        @NamedEntityGraph(name = "graph.Index.country",
                attributeNodes = {@NamedAttributeNode(value = "country")})
})

This part of code is added to class and then used by @Hint("graph.Index.countrySymbols") annotation.
You can find more [ref. 3]

Ad 6.
Hibernate requires default constructor (even private). Even using spring 4+ and Hibernate 5.2 I've got exception:
Caused by: org.hibernate.InstantiationException: No default constructor for entity: entity.Bill. 
It is a little strange for me because problem with instance creation, which was common for cglib, was resolved in Spring 4+ so I expected it should involve jpa entities as well.

Ad 7.
Hibernate ignores final type of variable in entity, especially it is noticeable when entity has defined some collection, initialised by the way of declaration. Chosen in declaration HashSet is replaced by hibernate implementation PersistentSet.

Resources:

  1. https://stackoverflow.com/questions/37282850/hibernate-org-hibernate-loader-multiplebagfetchexception-cannot-simultaneousl
  2. https://stackoverflow.com/questions/13334831/multiplebagfetchexception-thrown
  3. https://www.thoughts-on-java.org/jpa-21-entity-graph-part-1-named-entity/
  4. https://martinsdeveloperworld.wordpress.com/2014/07/02/using-namedentitygraph-to-load-jpa-entities-more-selectively-in-n1-scenarios/
  5. https://stackoverflow.com/questions/17987638/hibernate-one-to-one-lazy-loading-optional-false
  6. https://vladmihalcea.com/2016/07/26/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/

Monday, 26 June 2017

This week 12/2017

In this post I'd like to present handy pattern which is used to selenium test called "PageObject". Truly, it is a facade pattern which hides technical aspects of html and javascript content and shares only business friendly API.
Example PageObject bellow:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@PageObject
public class PtfPageObject {

    private static final String MENU_SECTION = "//table[@ng-controller='AppController']";

    @FindBy(xpath = MENU_SECTION + "//tr[@id='summarize']/td[8]/span")
    private WebElement profitAmount;

    @Autowired
    private WebDriver webDriver;

    public String findProfitAmount(){
        TimeoutUtils.waitForAngularJS(webDriver);
        return profitAmount.getText();
    }
}

I have turned this topic up after a half year. That time I used it to test web pages supported by JSP, ZK Framework or AngularJS pages and I found solution to synchronize an asynchronously loaded content.
Solution for AngularJS bellow:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static final int ANGULARJS_TIMEOUT = 30;
private static final int WAIT_FOR_NEXT_CHECK = 1;
public static final Function<JavascriptExecutor, Boolean> angularJSInProgress = (JavascriptExecutor drv) -> {
    String ngFinishedAllRequests = "var pendingRequests = angular.element(document.body).injector().get('$http').pendingRequests;"
            + " return (pendingRequests.length === 0)";
    return (boolean) drv.executeScript(ngFinishedAllRequests);
};

public static void waitForAngularJS(WebDriver webDriver, int secondTimeout) {
    Stopwatch stopwatch = Stopwatch.createStarted();
    boolean inProgress = angularJSInProgress.apply((JavascriptExecutor) webDriver);
    while (!inProgress) {
        inProgress = angularJSInProgress.apply((JavascriptExecutor) webDriver);
        if(stopwatch.elapsed(TimeUnit.SECONDS) > secondTimeout){
            throw new TimeoutException("Timeout occured - " + stopwatch.elapsed(TimeUnit.SECONDS));
        }
        try {
            TimeUnit.SECONDS.sleep(WAIT_FOR_NEXT_CHECK);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

You can use the method waitForAngularJS to wait for a response from server and do not try to guess how long it can take.
In the same way I resolved synchronization with VueJS.
I didn't try to resolve connection state in case of jquery or native request in a different way than add counter of begun connections. Maybe such information is available somewhere in browser driver.

Thursday, 15 June 2017

This week 11/2017

After years of programming I have seen good presentations of Kamil SzymaÅ„ski and Jakub Nabrdalik, who in a easy way explained what is a clean code and how to implement DDD in your code. I always was listening to SÅ‚awek Sobótka but I  had never known how to write such code until I took a part in Jakub Nabrdalik presentation about class's scopes and hexagonal architecture. Jakub was pointing popular mistakes made by all kind of Java developers as:

- splitting code between packages which describe layers, not functionality

- publication all code of module instead of publish only classes which we want allow to be used by other modules,

- creating a lot of test and affection to them what made production code not refactorable,

- focusing on algorithm testing instead of testing module as black box using behavioural or acceptation tests.

Easy example shows that we should put all services and repositories to one package with package scope and don't publish them to the world. Only fasad interface and dto can be published. This way we care only about contract between fasad and its client. This is the place to focuses on test which check all module and its behaviours. Jakub thinks that unit tests which test internal classes of module could be always deleted if there is difficult to correct them after module's code refactoring.

However Kamil noticed that unit tests​ should test units, not classes, that's why we should care of test code quality the same as about production code.


Resources:

Tuesday, 23 May 2017

This week 10/2017 - TestNG vs JUnit

In this article I'd like to compare two test Java framework. I have always used JUnit with some third party libraries but I didn't know all JUnit features. In this article I will list them all (all described on JUnit web side).

JUnit was probably the first Java unit test framework (I don't know any which has its beginning in 2002. Then in 2004 Cédric Beust frustrated of JUnit deficiencies, has created TestNG. In this article refers to JUnit (v 4.12) and TestNG (v 6.10) but new version of JUnit is in progress.

Both frameworks are dedicated to create unit, functional and integration tests but with TestNG it is all possible without any third party libraries. TestNG is more configurable in case of grouping and ordering of test execution.

The main reason why TestNG was created is that creator of TestNG was frustrated that JUnit creates a new instance of class for each test and he changed this approach in the TestNG to create one instance of a test class for each test methods in this class. This has some consequences - JUnit class is state full - contains class attributes which are refreshed before each test method. In case of TestNG class's attributes share some state for all test methods in a class. This assumption allows to create in an easy way test sequence.

Both frameworks allows to categorise and group tests. However the TestNG  is much more flexible and allows to create some sequence of tests.

Bellow I gathered most important advantages of each framework and attached some simple examples of use it.


TestNG (v 6.10)
1. Dedicated for unit, functional, integration tests.
2. Make dependences between methods and groups in one class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Test()
public void testZAdd() throws Exception {
    calc.addMemory(BigDecimal.TEN);
    Assert.assertEquals(calc.getMemory(), BigDecimal.TEN);
}

@Test(dependsOnMethods = {"testZAdd"})
public void testAdd2() throws Exception {
    calc.addMemory(BigDecimal.TEN);
    Assert.assertEquals(calc.getMemory(), new BigDecimal(20));
}

@Test(dependsOnMethods = {"testZAdd", "testAdd2"})
public void testClean() throws Exception {
    Assert.assertEquals(calc.getMemory(), new BigDecimal(20));
    calc.cleanMemory();
    Assert.assertEquals(calc.getMemory(), new BigDecimal(0));
}

3. One class can be supported by one thread

4. To organise all test in a testng.xml file - something like suite case in JUnit

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<test name="MyTestGroup">
  <groups>
    <run>
      <exclude name="brokenTests"  />
      <include name="checkinTests"  />
    </run>
  </groups>
  
  <classes>
    <class name="test.IndividualMethodsTest">
      <methods>
        <include name="testMethod" />
      </methods>
    </class>
  </classes>
</test>

5. Support input parameters of test method.

 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
@DataProvider(name = "test1")
public Object[][] createData1() {
    return new Object[][] {
            { null, null, BigDecimal.ONE, BigDecimal.TEN},
            { BigDecimal.TEN, new BigDecimal(2), new BigDecimal(4), new BigDecimal(4)},
    };
}

@DataProvider(name = "test2")
public Object[][] createData2() {
    return new Object[][] {
            { null, null, BigDecimal.ONE, BigDecimal.TEN},
            { new BigDecimal(12), new BigDecimal(2), new BigDecimal(2), new BigDecimal(3)},
    };
}

@org.testng.annotations.Test(dataProvider = "test1")
public void testAdd(BigDecimal sum, BigDecimal a, BigDecimal b, BigDecimal c) throws Exception {
    Assert.assertEquals(ComplexOperations.add(a, b, c), sum);
}

@org.testng.annotations.Test(dataProvider = "test2")
public void testMultiply(BigDecimal sum, BigDecimal a, BigDecimal b, BigDecimal c) throws Exception {
    Assert.assertEquals(ComplexOperations.multiply(a, b, c), sum);
}

6. Reusable of methods and classes.
7. Support parallel testing: per class, method, instance, etc. - It is possible to execute one method in parallel many times, ex. 60 times in 10 threads.


1
2
3
4
5
@Test(dependsOnMethods = {"testClean"}, threadPoolSize = 10, invocationCount = 60, timeOut = 1000)
public void testAfterClean() throws Exception {
    System.out.println("To " + Thread.currentThread().getId() + " -> " + this);
    Assert.assertTrue(true);
}

or by test.xml


1
<suite name="My suite" parallel="classes" thread-count="5">

8. Inject dependences alone or by an external library ex. by Guice


JUnit (v. 4.12)
1. Categorisation of test ??class or method??
2. Parametrisation of test but test class instance have to be statefull.


 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
@RunWith(Parameterized.class)
public class ComplexOperationsJUnitDataProvider {

    private BigDecimal sum;
    private BigDecimal a;
    private BigDecimal b;
    private BigDecimal c;

    @Parameterized.Parameters
    public static Object[][] createData1() {
        return new Object[][]{
                {null, null, BigDecimal.ONE, BigDecimal.TEN},
                {BigDecimal.TEN, new BigDecimal(2), new BigDecimal(4), new BigDecimal(4)},
        };
    }

    public ComplexOperationsJUnitDataProvider(BigDecimal sum, BigDecimal a, BigDecimal b, BigDecimal c) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.sum = sum;
    }

    @Test
    public void testAdd() throws Exception {
        Assert.assertEquals(ComplexOperations.add(a, b, c), sum);
    }
}

3. Basic test ordering by @FixMethodOrder(MethodSorters.NAME_ASCENDING)
4. Assumption to tested code. If assumptions is false then skip test.


1
2
3
4
5
6
@Test
public void testAdd1() throws Exception {
    Assume.assumeThat(calc.getMemory(), is(BigDecimal.ZERO));
    calc.addMemory(BigDecimal.TEN);
    Assert.assertEquals(calc.getMemory(), new BigDecimal(10));
}

5. Continues testing - external library. - This solution execute a test in background after it discover change in code.
6. Concurrent test - ConcurrentUnit
7. Ignore test

1
2
3
@Ignore
@Test
public void testMyAlgorythm(){ ... }

Test class with 2 test methods, one with Ignore annotation, gives following result
Tests run: 2, Failures: 0, Errors: 0, Skipped: 1


8. Timeout for test

1
2
@Test(timeout = 10)
public void testMyAlgorythm() { ...}

9. Rules in test class body
10. @Theory mechanism - helps with generation of input data


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@RunWith(Theories.class)
public class CalculatorWithMemoryJUnit {

    private CalculatorWithMemory calc = new CalculatorWithMemory();

    @Theory
    public void testSum(@TestedOn(ints = {0, 10, 30, 60}) int val, @TestedOn(ints = {0, 10, 30, 60}) int sum) {
        Assume.assumeThat(val, is(sum));
        System.out.println("Test with val " + val + ", sum=" + sum);
        Assume.assumeThat(calc.getMemory(), is(BigDecimal.ZERO));
        calc.addMemory(new BigDecimal(val));
        Assert.assertEquals(0, new BigDecimal(sum).compareTo(calc.getMemory()));
    }
}

Next time I am going to present mock frameworks.



Wednesday, 3 May 2017

This week 9/2017 - The REST client API

There are a few implementation of REST client for Java. I will compare 3, I think, most popular implementations.
  1. Jersey (v. 2.25.x)
  2. Spring Web (v. 3.2.x)
  3. Apache CXF (v. 3.0.x)
My goal is to create component which can:
  1. Connect by secure channel - SSL
  2. Authentication bu Basic authorization mechanism.
  3. Send custom message.
  4. Read response and deserialize it into object.
So most common use of client. I was pointing to: flexible interface, clean code - nothing special. So I created following Java code


 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.util.Arrays;
import java.util.Base64;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import org.apache.cxf.jaxrs.client.WebClient;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

public class App {

 // Apache CXF
 private static WebClient createWebClient() {
  return WebClient.create(getUrl(), "aa", "bbc", null);
 }
 private void testWebClient(){
  System.out.println("\n\ntestWebClient:");
//  System.out.println(createWebClient().path("todo22").accept(MediaType.TEXT_XML).get(Todo2.class));
  System.out.println(createWebClient().path("todo22").get(Todo2.class));
  System.out.println(createWebClient().path("todo22").get(String.class));
 }

 // Jersey
 private static Client createWebTarget() {
  HttpAuthenticationFeature authenticationFeature = HttpAuthenticationFeature.basic("aa", "bbc");
  return ClientBuilder.newBuilder().newClient().register(authenticationFeature);
 }

 private void testWebTarget(){
  WebTarget service = createWebTarget().target(getUrl());
  System.out.println("\n\ntestWebTarget :");
//  System.out.println(service.path("todo22").request(MediaType.TEXT_XML).get(Todo2.class));
  System.out.println(service.path("todo22").request().get(Todo2.class));
  System.out.println(service.path("todo22").request().get(String.class));
 }

 // Spring Web Rest
 private static HttpHeaders getHeaders(){
  String plainCredentials="aa:bbc";
  String base64Credentials = new String(Base64.getEncoder().encode(plainCredentials.getBytes()));

  HttpHeaders headers = new HttpHeaders();
  headers.add("Authorization", "Basic " + base64Credentials);
  headers.setAccept(Arrays.asList(org.springframework.http.MediaType.TEXT_XML));
  return headers;
 }

 private void testRestTemplate(){
  System.out.println("\n\ntestRestTemplate :");
  RestTemplate restTemplate = new RestTemplate();

  // restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
  restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
  HttpEntity<String> request = new HttpEntity<String>(getHeaders());
  System.out.println(restTemplate.exchange(getUrl() + "todo22", HttpMethod.GET, request, Todo2.class).getBody());
  System.out.println(restTemplate.exchange(getUrl() + "todo22", HttpMethod.GET, request, String.class).getBody());
 }

In my opinion the simplest interface has Apache CXF but you need to remember to create new instance for multiple services.
Jersey interface looks to be it most powerful. At the beginning there is created some service client template - core of client. Then every time when path method is executed there is created new instance of client with resource name.
Jersey support all kind of authentication methods providing tool. It is opposite to Spring Web RestTemplate where I had to prepare manually authorization header. Spring Web REST required much more code then two other solutions.

Resources:

This week 8/2017

In this post I will write two small tips.

Slf4J - has one handy feature. One interface solves two problems:
1) firstly it check if message will be used, then transform parameters to String type and build all message,
2) helps to have clean code without concatenation of parameters

LOGGER.debug("Test message {} {} {}{}{}{}{}{}{}", 3, "+", 3, =, null, " 6");

Earlier I use to check logging level manually and use to format messages by String::printf


Lombok - builder pattern is great pattern to create immutable value object with many attributes. Lombok does it for you, you have only to add @Builder annotation. Unfortunately it has other standard of calling setter method (there is no set prefix) and it is useless to create builder for inheritance’s class.

Bellow I compare simple class with lombok annotations and equivalent to it.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;

@Data
@Builder
public class MyLoombok {

 @NonNull
 private final String attr1;

 @NonNull
 private final int attr2;
}


Generated code of builder.
 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import lombok.NonNull;

public class MyLoombok {

 @NonNull
 private final String attr1;

 @NonNull
 private final int attr2;

 @java.beans.ConstructorProperties({"attr1", "attr2"})
 MyLoombok(String attr1, int attr2) {
  this.attr1 = attr1;
  this.attr2 = attr2;
 }

 public static MyLoombokBuilder builder() {
  return new MyLoombokBuilder();
 }

 @NonNull
 public String getAttr1() {
  return this.attr1;
 }

 @NonNull
 public int getAttr2() {
  return this.attr2;
 }

 public boolean equals(Object o) {
  if (o == this) return true;
  if (!(o instanceof MyLoombok)) return false;
  final MyLoombok other = (MyLoombok) o;
  if (!other.canEqual((Object) this)) return false;
  final Object this$attr1 = this.getAttr1();
  final Object other$attr1 = other.getAttr1();
  if (this$attr1 == null ? other$attr1 != null : !this$attr1.equals(other$attr1)) return false;
  if (this.getAttr2() != other.getAttr2()) return false;
  return true;
 }

 public int hashCode() {
  final int PRIME = 59;
  int result = 1;
  final Object $attr1 = this.getAttr1();
  result = result * PRIME + ($attr1 == null ? 43 : $attr1.hashCode());
  result = result * PRIME + this.getAttr2();
  return result;
 }

 protected boolean canEqual(Object other) {
  return other instanceof MyLoombok;
 }

 public String toString() {
  return "singleclass.MyLoombok(attr1=" + this.getAttr1() + ", attr2=" + this.getAttr2() + ")";
 }

 public static class MyLoombokBuilder {
  private String attr1;
  private int attr2;

  MyLoombokBuilder() {
  }

  public MyLoombok.MyLoombokBuilder attr1(String attr1) {
   this.attr1 = attr1;
   return this;
  }

  public MyLoombok.MyLoombokBuilder attr2(int attr2) {
   this.attr2 = attr2;
   return this;
  }

  public MyLoombok build() {
   return new MyLoombok(attr1, attr2);
  }

  public String toString() {
   return "singleclass.MyLoombok.MyLoombokBuilder(attr1=" + this.attr1 + ", attr2=" + this.attr2 + ")";
  }
 }
}