2014/09/21

Lookup Methods in Enums

There is often the need to map numeric values of a Java enum to the enum instances, aka lookup.
It seems as a simple task yet I saw and even used different approaches until finally came with something acceptable. Below is short comparison of witnessed approaches, ending with what I see as the best one.



First the list approaches I tried (or witnessed) and refused.
In the following examples the numeric value to look-up is in variable code, method getCode() returns the numeric value of the enumeration instance.

Rummaging Through the Values Using For Loop

for(EnumType value : values)
{ 
  if (value.getCode()==code) { return value; } 
}

Pros: zero maintenance
Cons: waste of cycles

Look-up Values Hard-Coded in the Switch Statement

public static EnumType fromInt (int code) 
{
  EnumType result = UNKNOWN; // fallback value
  switch (code)
  {
    case 1: result = ENUM_ONE; break;
    //... 
  }
  return result;
}

Pros: fast lookup
Cons: needs maintenance, needs test for assuring the conversion is reflexive (all known values are converted to proper instances)

Storing Values in Hard-Coded Map

private static Map<Integer,EnumType> lookupMap = new HashMap<>()
static 
{
  lookupMap.put(1,ENUM_ONE);
  lookupMap.put(2,ENUM_TWO);
}

public static EnumType fromInt (int code) 
{
   return lookupMap.get(code);
} 
 
Pros: fast lookup
Cons: needs maintenance


Using Load-Time Constructed Map

The solution merges fast lookup with zero maintenance - it is based on a Map populated with values in static initialization block. This is possible to do for enums as at the moment when the map is going to be filled, all the enum constants are already instantiated (guaranteed by JVM).
private static Map<Integer,EnumType> lookupMap;

static
{ 
  EnumType[] values = values();
  lookupMap = new HashMap<Integer,EnumType>(values.length);
  for (EnumType value : values)
  {
    lookupMap.put(value.getCode(),value);
  }
}

public static EnumType fromInt (int code) 
{
   return lookupMap.get(code);
}

2014/07/17

Git with Attlasian Stash via SSH, on Windows

I do not know why Atlassian decided to name the thing Stash when there was  Git stash, probably some "clever" marketing.

I used Git for some time but just recently I had to use the SSH protocol on Windows  to authenticate to Stash. Integrating Linux-born software to Windows environment is sometimes easy but that was not this case - to prevent more people to curse Git because of that, I made the following HOWTO.

Install Git

First of all you need to install Git and configure access using SSH protocol.  I decided to not try anything fancy and downloaded the standard git client installation file. There is several dialogs during installation, I recommend to use "Run Git from Windows Command Prompt" in the "Adjusting PATH Environment"second dialog. The most important option is "Choosing SSH executable" - select "Use OpenSSH" - the Git will then use OpenSSH included in GIT installation.

I installed Git into directory C:\git, feel free to choose installation path that suites your needs. I would recommend to avoid using path wit space though.

When finished, open powershell console and go to directory to C:\git\bin.

Generate SSH Keys 

Following command generates new pair of keys (private will stay with you and public will be given later to Stash for you authentication).

C:\git\bin> .\ssh-keygen.exe -t rsa -C your.email@address.com

Set basic key file name to C:\git\bin\id_rsa when asked for the keys. To make things simple, use empty pass-phrase (you can fortify your setup later when you get more familiar with OpenSSH). The conversation with ssh-keygen should look like this:

Generating public/private rsa key pair.
Enter file in which to save the key (//.ssh/id_rsa): C:\git\bin\id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\git\bin\id_rsa.
Your public key has been saved in C:\git\bin\id_rsa.pub.


Configure Bundled OpenSSH

Create directory C:\git\.ssh and move the newly created files id_rsa (private key) and id_rsa.pub (public key) into it. Then create file C:\git\.ssh\config with following content:

Host stash.atlassian.com
    HostName stash.atlassian.com
    PreferredAuthentications publickey
    IdentityFile /.ssh/id_rsa


In case your company has its own Stash instance running, adapt the Host and HostName options accordingly.

Submit Public Key to Stash

Login to your Stash account.  In the top right corner, from select from drop-down menu "Manage account".  On left side of the page you should now see several links, click on "SSH Keys".

Put content of your public key (id_rsa.pub) into the text of the provided form. Do not edit it the content at all. Removing, regardless if deliberate or accidental, of some  white space characters, namely line breaks does not prevent Stash to accept the key but the authentication will fail.

Test Connection

First test the connection using only OpenSSH:

C:\git\bin> .\ssh.exe -v -T git@stash.atlassian.com -p 7999

If everything works well, the output you get ends with sentences "Authentication succeeded." and probably also "Entering interactive session." If so, you can move C:\git\bin\.ssh directory to C:\your_windows_login\ directory and retest that the connection still gets authenticated with the files in new location:

C:\git\bin\ssh.exe -v -T git@stash.atlassian.com -p 7999

Clone

If everything worked as described so far you should be now able clone your Stash-hosted repository using instructions available from Stash profile or project page.


2014/05/12

Testing REST Service in Spring Boot with RestTemplate

I have decided to add my bit to Spring Boot's well-deserved popularity -- one must appreciate how fast you can create a simple application without compromising the design.

Of course, if you take your work seriously there is no way you can get by without  tests. An example is better than hours of explaining -- below you see testing of real service (no mocks) running locally:
  • in first case sending POST request to create a new record and creating an object to from the response
  • in second case GET request is send and the failure message is treaded as "raw" JSON


import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Collections;

import static org.hamcrest.Matchers.*;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;


@RunWith (SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration (classes = Config.class)
@WebAppConfiguration
@IntegrationTest
public class UserControllerTest
{
  final String BASE_URL = "http://localhost:9999/customer/";

  @Test
  public void shouldCreateNewUser() {

    final String USER_NAME = "Leif Ericson";
    final String USER_ADDRESS = "Vinland";

    User user = new User();
    user.setName(USER_NAME);
    user.setAddress(USER_ADDRESS);

    RestTemplate rest = new TestRestTemplate();

    ResponseEntity<User> response = 
       rest.postForEntity(BASE_URL, user, User.class, Collections.EMPTY_MAP);
    assertThat( response.getStatusCode() , equalTo(HttpStatus.CREATED));

    User userCreated = response.getBody();
    assertThat( userCreated.getId() , notNullValue() );
    assertThat( userCreated.getName() , equalTo(CUSTOMER_NAME) );
    assertThat( userCreated.getAddress() , equalTo(CUSTOMER_ADDRESS) );
  }

  @Test
  public void shouldFailToGetUnknownUser()
  throws IOException {

    final int UNKNOWN_ID = Integer.MAX_VALUE;
    final String EXPECTED_ANSWER_MESSAGE = "user with id : '" + UNKNOWN_ID+ "' does not exist";

    RestTemplate rest = new TestRestTemplate();

    ResponseEntity<String> response = rest.getForEntity(BASE_URL + UNKNOWN_ID, String.class);

    assertThat( response.getStatusCode() , equalTo(HttpStatus.NOT_FOUND));

    ObjectMapper objectMapper = new ObjectMapper();
    JsonNode responseJson = objectMapper.readTree(response.getBody());
    JsonNode messageJson = responseJson.path("message");

    assertThat( messageJson.isMissingNode() , is(false) );
    assertThat( messageJson.asText() , equalTo(EXPECTED_ANSWER_MESSAGE) );
  }
}