Monthly Archives: December 2013

IntelliJ: Preventing Wildcard Imports

PROBLEM

I have been getting the following PMD warning that I can’t seem to suppress with PMD’s @SuppressWarnings

RequestServiceImpl.java:1, UnnecessaryFullyQualifiedName, Priority: Normal

Unnecessary use of fully qualified name 'com.choonchernlim.epicapp.service.impl' 
due to existing import 'com.choonchernlim.epicapp.service.*'.

The reason I’m getting this warning is because IntelliJ will automatically replace all single imports from the same package with a wildcard ‘*’ when the class count hits a certain limit.

By using wildcard imports, it also becomes difficult for my peers to understand where the classes are coming from.

SOLUTION

To fix this permanently in IntelliJ:-

  1. Go to Preferences…Code StyleJava.
  2. Go to Imports tab.
  3. Check Use single class import
  4. Set a ridiculous high number on Class count to use import with ‘*’ and Names count to use static import with ‘*’ because these fields cannot be empty. In my case, I set 100 for each field.
  5. Select OK.

Finally, perform an Optimize Imports… on that Java file in question.

Advertisements

Modernizr: Detecting Touch Devices vs Non Touch Devices

Modernizr has a great feature that allows us to detect whether the browser is running on a touch device (iPad, smartphones, etc) or on a non-touch device (laptop, computer, etc).

Even though most modern touch devices support “proximity” hovering (navigating without touching the device screen), the browsing experience still suffer when dealing with onmouseover and onmouseout events.

So, let’s assume we want to implement WordPress’ Posts table. When we hover over any table row on a non-touch device, it will show additional actions such as “Edit”, “Quick Edit”, “Trash” and “View”. When we view the same page on a touch device, it will show the action without the hovering option.

The table may look something like this:-

<table class="table-actionable">
    <tr>
        <th>Artist</th>
        <th>Song Title</th>
    </tr>
    <tr>
        <td>System Of a Down</td>
        <td>Toxicity
            <ul class="hidden-action hide">
                <li><a href="/epic-app/buy/1">Buy</a></li>
                <li><a href="/epic-app/sell/1">Sell</a></li>
                <li><a href="/epic-app/trash/1">Trash</a></li>
            </ul>
        </td>
    </tr>
    <tr>
        <td>Eminem</td>
        <td>Rap God
            <ul class="hidden-action hide">
                <li><a href="/epic-app/buy/2">Buy</a></li>
                <li><a href="/epic-app/sell/2">Sell</a></li>
                <li><a href="/epic-app/trash/2">Trash</a></li>
            </ul>
        </td>
    </tr>
</table>

With Modernizr, we can now write something like this:-

<script>
    // On non-touch devices, show action list on mouse over and hide it on mouse out.
    if ( !Modernizr.touch ) {
        $( '.table-actionable tr' )
                .mouseover( function () {
                                $( this ).find( '.hidden-action' ).removeClass( 'hide' );
                            } )
                .mouseout( function () {
                               $( this ).find( '.hidden-action' ).addClass( 'hide' );
                           } );
    }
    // On touch devices, always show action list.
    else {
        $( '.hidden-action' ).removeClass( 'hide' );
    }
</script>

Shitty epic…

Java: Invoking Secured Web Service with JSESSIONID

PROBLEM

I wrote a JSP custom tag that invokes a secured web service within the same application to perform some evaluation. This custom tag is only used in the secured views where the user has successfully authenticated against Spring Security, and they have access to these views. The secured web service is also guarded by Spring Security.

My custom tag implementation looks something like this:-

public int doStartTag() throws JspException {
	
	...
	
	HttpClient httpclient = HttpClientBuilder.create().build();
	
	HttpGet httpGet = new HttpGet(url);

	log.debug("Before executing HTTP Get...");

	HttpResponse response = httpclient.execute(httpGet);

	int statusCode = response.getStatusLine().getStatusCode();
	
	log.debug("Status Code: " + statusCode);

	return statusCode == HttpStatus.SC_OK ? EVAL_BODY_INCLUDE : SKIP_BODY;
}

The goal here is… if the response code from the secured web service is a 200 OK, then the custom tag will evaluate its body and include the content. Otherwise, it will hide it.

My first attempt is to test the custom tag with an invalid value so that the secured web service will throw a 400 Bad Request. The hope is the custom tag’s body will not get rendered in the view.

After getting authenticated successfully by Spring Security, the view containing this custom tag gets rendered. However, the response code always return a 200 OK even though I should get a 400 Bad Request from the secured web service.

After poking around, I decided to debug the console log… and this is what I see:-

[DEBUG] [CheckEditAccess] [hasAccess:72] - Before executing HTTP Get...
127.0.0.1 -  -  [29/Dec/2013:10:18:14 -0600] "GET /epic-app/api/request/invalid-key 
HTTP/1.1" 302 0 
127.0.0.1 -  -  [29/Dec/2013:10:18:14 -0600] "GET /epic-app/;jsessionid=1vlnl6eloabfh12r9x465kvuq 
HTTP/1.1" 200 6311 
[DEBUG] [CheckEditAccess] [hasAccess:75] - Status Code: 200

As you can see, there are two calls made even though my custom tag only makes one call. The first call is made by the custom tag but the response code is a 302 Found. Then, it gets directed to the next URL that returns a 200 OK.

Why is this happening?

SOLUTION

The problem here is because when the custom tag invokes the secured web service call, it is not done within the same session where the user is already authenticated by Spring Security. Thus, Spring Security prevents the actual call to go through. Instead, Spring Security redirects the request to the login page first (which is located at /epic-app in this case). As a result, the custom tag will receive a 200 OK from the login page instead of the response code from the secured web service.

To fix this, we need to send the session ID with the HTTP Get before executing it:-

public int doStartTag() throws JspException {
	
	...
	
	CookieStore cookieStore = new BasicCookieStore();
    BasicClientCookie cookie = new BasicClientCookie("JSESSIONID", pageContext.getSession().getId());
    cookieStore.addCookie(cookie);
	
	// very important to set the domain, otherwise this will not work!
    cookie.setDomain(request.getServerName());

    cookieStore.addCookie(cookie);

    HttpClient httpclient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build();
	
	HttpGet httpGet = new HttpGet(url);

	log.debug("Before executing HTTP Get...");

	HttpResponse response = httpclient.execute(httpGet);

	int statusCode = response.getStatusLine().getStatusCode();
	
	log.debug("Status Code: " + statusCode);

	return statusCode == HttpStatus.SC_OK ? EVAL_BODY_INCLUDE : SKIP_BODY;
}

When I try it again, now I’m getting the intended 400 Bad Request from the secured web service due to input validation error.

[DEBUG] [CheckEditAccess] [hasAccess:72] - Before executing HTTP Get...
127.0.0.1 -  -  [29/Dec/2013:10:45:19 -0600] "GET /epic-app/api/request/invalid-key 
HTTP/1.1" 400 0 
[DEBUG] [CheckEditAccess] [hasAccess:73] - Status Code: 400

Java: Performing 2-Key Lookup using HashBasedTable

PROBLEM

HashMap is great to perform a simple lookup, for example:-

Map<String, String> map = new HashMap<String, String>();
map.put("key", "mike");

String value = map.get("key"); // "mike"

However, what if we need 2 keys to lookup a value? For example, we need “key1” + “key2” in order to lookup “mike”.

SOLUTION 1: Create combo key

You can combine the keys to create a unique key:-

Map<String, String> map = new HashMap<String, String>();
map.put("key1|key2", "mike");

String value = map.get("key1|key2"); // "mike"

Assuming if the keys are just characters, this works fine. However, this solution might not work too well if the keys are objects, like Person object, etc.

SOLUTION 2: Create key as an object

In this solution, we leverage Apache Commons’ MultiKey to create the combo key:-

Map<MultiKey, String> map = new HashMap<MultiKey, String>();
map.put(new MultiKey("key1", "key2"), "mike");

String value = map.get(new MultiKey("key1", "key2")); // "mike"

This solution works a little better than the first solution, but the code looks just plain ugly to read.

SOLUTION 3: Using Map of Map

In this solution, we use Map of Map to accomplish the 2-key lookup:-

Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>();

Map<String, String> innnerMap = new HashMap<String, String>();
innnerMap.put("key2", "mike");

map.put("key1", innnerMap);

String value = map.get("key1").get("key2"); // "mike"

This solution obviously offers tons of flexibility, but it is shit difficult to read… and sadly, I myself implemented something like this in the past. In my opinion, this solution is too complicated for what we are trying to achieve here. We also have to worry about whether map.get(firstKey) will return null, which may cause NullPointerException if we chain the call to lookup the second key.

SOLUTION 4: Using HashBasedTable

The most elegant solution in my opinion is to utilize Google Guava’s HashBasedTable:-

HashBasedTable<String, String, String> hashBasedTable = HashBasedTable.create();
hashBasedTable.put("key1", "key2", "mike");

String value = hashBasedTable.get("key1", "key2"); // "mike"

This is probably the cleanest solution to perform 2-key lookup. Based on the documentation, HashBasedTable itself is using Map of Map under the cover.

Jadira Usertype: Under JDK 6 it may not be possible to handle DST transitions correctly

PROBLEM

When saving a Hibernate entity that contains Joda Time object, Jadira UserType throws the following warning:-

[WARN ] [JavaTimeZoneWorkaroundHelper] [<clinit>:40] - Under JDK 6 it may not be possible 
to handle DST transitions correctly
[ERROR] [JavaTimeZoneWorkaroundHelper] [<clinit>:42] - Running under a Zone that uses 
daylight saving time. To avoid incorrect datetimes being stored during DST transition, 
either update to JDK 7 or use a Timezone for the JDK without Daylight Saving Time

This warning occurs when using this version:-

<dependency>
    <groupId>org.jadira.usertype</groupId>
    <artifactId>usertype.core</artifactId>
    <version>3.0.0.GA</version>
</dependency>

SOLUTION

Upgrade Jadira Usertype to the latest version, and the problem goes away:-

<dependency>
    <groupId>org.jadira.usertype</groupId>
    <artifactId>usertype.core</artifactId>
    <version>3.1.0.CR10</version>
</dependency>

Hibernate + Joda Time: Auto Registering Type

OVERVIEW

Jadira Usertype is required when using Joda Time with Hibernate 4. The Joda Time objects are annotated accordingly in the Hibernate entity, and they looks something like this:-

@Entity
@Table(name = "person")
public class Person implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "personId")
    private Long id;

    @Column
    private String name;
	
    @Column
    @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
    private LocalDate birthDate;

    @Column
    @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
    private LocalDateTime createdDatetime;
	
    // getters and setters
}

PROBLEM

This solution works, but it is rather tedious because I can never remember the actual @Type to write, thus I always end up copying and pasting it from past project code.

Further, that additional annotations clutter up my otherwise beautiful code.

SOLUTION

Jadira Usertype provides a clean way to auto register these Joda Time object types.

In the Hibernate configuration, add the following line:-

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>

    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
            <prop key="hibernate.show_sql">false</prop>
            <prop key="jadira.usertype.autoRegisterUserTypes">true</prop>
        </props>
    </property>
    <property name="packagesToScan">
        <list>
            <value>com.choonchernlim.project.entity</value>
        </list>
    </property>
</bean>

Now all @Type annotations can be safely removed from the Hibernate entity:-

@Entity
@Table(name = "person")
public class Person implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "personId")
    private Long id;

    @Column
    private String name;
	
    @Column
    private LocalDate birthDate;

    @Column
    private LocalDateTime createdDatetime;
	
    // getters and setters
}

IntelliJ: Configuring SVN Ignored Files

OVERVIEW

Whenever we share a project in SVN or checks out a project from SVN, we have to perform a one-time configuration on the project to ignore selected files from being committed into SVN. That said, this configuration will only work if the files are not already in SVN.

PROBLEM

Let’s assume we have a Maven project that contains an EAR module and a WAR module:-

EpicApp
├── .idea
│   └── ...
├── EpicApp.iml
├── epicapp-ear
│   ├── epicapp-ear.iml
│   ├── pom.xml
│   └── src
│       └── ...
├── epicapp-war
│   ├── epicapp-war.iml
│   ├── pom.xml
│   └── src
│       └── ...
└── pom.xml

IntelliJ generates .idea folder and *.iml files to store our project settings and module configuration.

When we run mvn clean compile, the target folders are created:-

EpicApp
├── .idea
│   └── ...
├── EpicApp.iml
├── epicapp-ear
│   ├── epicapp-ear.iml
│   ├── pom.xml
│   ├── src
│   │   └── ...
│   └── target
│       └── ...
├── epicapp-war
│   ├── epicapp-war.iml
│   ├── pom.xml
│   ├── src
│   │   └── ...
│   └── target
│       └── ...
├── pom.xml
└── target # Depending on how the root pom.xml is configured, this folder may or may not appear.
    └── ...

If we look at the overall project structure, there are tons of generated files that are not created by us:-

EpicApp
├── .idea
│   └── ...
├── EpicApp.iml
├── epicapp-ear
│   ├── epicapp-ear.iml
│   ├── pom.xml
│   ├── src
│   │   └── ...
│   └── target
│       └── ...
├── epicapp-war
│   ├── epicapp-war.iml
│   ├── pom.xml
│   ├── src
│   │   └── ...
│   └── target
│       └── ...
├── pom.xml
└── target # Depending on how the root pom.xml is configured, this folder may or may not appear.
    └── ...

First, we definitely want to ignore target folders from being committed into SVN.

Now, regarding whether to ignore .idea folder and *.iml files, there are two camps out there:-

  • Camp PINK will commit the IntelliJ generated files into SVN so that the project configuration is shared among team members.
  • Camp BLUE will NOT commit files that are not created by them.

What’s my take? I’m with Camp BLUE because I don’t like pink in the first place. In the past, I tend to commit all IntelliJ generated files into SVN. However, it was a nightmare working in a team environment because every team member kept overwriting these files from one other, resulting endless SVN conflicts. Since we encourage folks to only commit the files they modify (or create), I see no absolute reason to commit these IntelliJ generated files. We saw more cons than pros based on our past experience.

SOLUTION 1: Add Ignored Files Using IntelliJ

Based on the example above, there are 5 things we need to ignore:-

  • epicapp-ear/target/ directory
  • epicapp-war/target/ directory
  • target/ directory
  • .idea/ directory
  • *.iml files

So, in IntelliJ…

  1. Select “Changes” tab at the bottom.
  2. Select “Local” subtab.
  3. Click on “Configure Ignored Files” button.
    "Changes" tab
  4. In “Configure Ignored Files” dialog, click on the “+” button.
  5. Add the ignored file (or directory).
    "Configure Ignored Files" dialog

  6. Repeat step 4 and 5 until all ignored files are added.

SOLUTION 2: Add Ignored Files Directly in .idea/workspace.xml

If you have quite a few files and directories to ignore, the above solution can be rather tedious.

So, an alternative solution is to “hack” .idea/workspace.xml by entering all the ignored paths under “ChangeListManager” component:-

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <component name="ChangeListManager">
        ...
        <ignored path="epicapp-war/target/" />
        <ignored path="epicapp-ear/target/" />
        <ignored path=".idea/" />
        <ignored path=".settings/" />
        <ignored path="target/" />
        <ignored path=".DS_Store" />
        <ignored path=".classpath" />
        <ignored path=".project" />
        <ignored mask="*.iws" />
        <ignored mask="*~" />
        <ignored mask="*.bak" />
        <ignored mask="*.log" />
        <ignored mask="*.iml" />
    </component>
    ...
</project>

In this example, we ignore all target folders, all IntelliJ generated files, all Eclipse generated files, all OS-specific generated files and some useless files.

When we save .idea/workspace.xml, IntelliJ immediately detects a change made to this file (because it is intelligent) and it will prompt us to reload the project. Once reloaded, the “Configure Ignored Files” dialog will reflect the changes made.