Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

Web developers were used to using cookies to store data in the user’s browser. You could then set how long you wanted the cookie to last. It could automatically be removed when the browser was closed or it could last longer. Overtime, cookies got a bad rap. There was also a relatively small size limitation.

HTML5 started a move away from cookies and to web storage. There are two types of web storage, sessionStorage and localStorage. Both of them use the same API signature, meaning that once you have an example of localStorage working, you also know how to access sessionStorage.

If they are both accessed the same way, then what is the difference? LocalStorage persists across browser sessions. You can think of it as persistent storage. LocalStorage is per origin Origin is defined as the protocol (e.g. http, https, etc), hostname and port number (e.g. 80, 443, 8080). This means that all of the pages from your website that are HTTPS can see the data set by another window, but the HTTP pages cannot see the data. This also means that if you are using multiple host names, you can’t share data across your domains.

SessionStorage has a shorter lifespan. It is intended for as long as the browser window is open. When the browser window is closed, this data is removed. Whereas localStorage is origin specific, sessionStorage is origin-window specific. Meaning, that even for the same domain, etc, the data is specific to the window that wrote it. This is something that cookies couldn’t support on their own. Any data that is written in sessionStorage is only visible by the window that wrote it.

Let’s look at how to use the web storage API. LocalStorage and sessionStorage are both accessed by global variables that are named after the type of storage, localStorage and sessionStorage. Meaning, that they are on the current window object (e.g. window.localStorage and window.sessionStorage). My examples will use localStorage, but the examples are the same for sessionStorage. In general, browsers allow up to 5MB to be stored. Much larger than the cookie size limit.

Does this browser support web storage

How do we determine if a browser supports localStorage? I saw two different ways. With that said, most browsers support web storage. IE8, FF 3.5, Chrome 4, Safari 4, Android 2.0, Chrome on Android 44.

        /**
         * Does this browser support HTML5 Local Storage.
         * @returns {boolean}
         */
        function supportsHtml5Storage() {
            try {
                return 'localStorage' in window && window['localStorage'] != null;
            } catch (e) {
                return false;
            }
        }

        /**
         * An alternative way to determine if a browser supports local storage.
         * @returns {boolean}
         */
        function supportsHtml5StorageAlternate() {
            return typeof(Storage) !== "undefined";
        }

Modernizer also has a method to determine if localStorage is supported.

// In FF4, if disabled, window.localStorage should === null.

// Normally, we could not test that directly and need to do a
//   `('localStorage' in window) && ` test first because otherwise Firefox will
//   throw bugzil.la/365772 if cookies are disabled

// Also in iOS5 & Safari Private Browsing mode, attempting to use localStorage.setItem
// will throw the exception:
//   QUOTA_EXCEEDED_ERRROR DOM Exception 22.
// Peculiarly, getItem and removeItem calls do not throw.

// Because we are forced to try/catch this, we'll go aggressive.

// Just FWIW: IE8 Compat mode supports these features completely:
//   www.quirksmode.org/dom/html5.html
// But IE8 doesn't support either with local files

Modernizr.addTest('localstorage', function() {
    var mod = 'modernizr';
    try {
        localStorage.setItem(mod, mod);
        localStorage.removeItem(mod);
        return true;
    } catch(e) {
        return false;
    }
});

How to write data

The data is stored in a key-value pair map. The value of the data is stored as a string. If you need it to be an integer, etc you will need to convert it to the value type that you need. The data is written by calling setItem. The example shows setting a value that is a string and an integer. Regardless, the value ends up as a string.

        /**
         * Sets a couple of items to local storage.
         */
        function setLocalStorageItem() {
            localStorage.setItem("foo", "bar");
            localStorage.setItem("max", 5);
        }

How to read data

Data is retrieved by calling the getItem method. Remember that the data comes back as a string and needs to be converted. Below are both examples. If the data was ‘5’, then retrieving the value of ‘5’ and adding 1 to it will result in ’51’ if you don’t convert the value to an integer. The value will be 6 if you convert it to an int, then add.

        /**
         * Gets an item from local storage.
         */
        function getLocalStorageAsString() {
            return localStorage.getItem("max") + 1;
        }

        /**
         * Gets an item from local storage.
         */
        function getLocalStorageAsNumber() {
            // local storage items are stored as a string
            return parseInt(localStorage.getItem("max")) + 1;
        }

How to remove data

Like most maps, there is the ability to remove an item from the map. However, unlike some other implementations, calling removeItem doesn’t return a value to indicate if it existed before it was removed.

        /**
         * Removes an item from local storage
         */
        function removeLocalStorage() {
            // removeItem does NOT return a value like Java does
            localStorage.removeItem("foo");
        }

There is also the option to remove all of the data that is in storage. This is accomplished by calling clear. I could not get this to work on Firefox 40.0.2.

        /**
         * Clear the contents of local storage.
         */
        function clearLocalStorage() {
            // clear did not work on FireFox 40.0.2
            localStorage.clear();
        }

How to query web storage for it’s contents

There is also the ability to get the number of items being stored in the local storage.

        /**
         * Gets the number of items in Local Storage.
         * @returns {number}
         */
        function getLocalStorageLength() {
            return localStorage.length;
        }

Once you have the number of items in a map, you can iterate over the items, get the key, then use the key to get the value.

        /**
         * Displays the contents of local storage.
         * @returns {string}
         */
        function displayLocalStorageContents() {
            var contents = "";
            var key = "";
            var value = "";
            for (var i = 0; i < localStorage.length; i++) {
                key = localStorage.key(i);
                value= localStorage.getItem(key);
                contents += "index=" + i + "\tkey=" + key + "\nvalue=" + value + "\n"
            }
            return contents;
        }

How to listen for storage events

Sometimes you will have the need to know that the storage has been changed. This will be especially useful if you are trying to coordinate the activities of multiple windows. You register an event listener by calling addEventListener, then implementing the a method to respond to the storage events. I didn’t see consistent behavior on this. I could call setItem, getItem and removeItem. I never got it to fire. I was able to make it fire in Chrome by manually changing the storage value. As of right now, the various browser implementations don’t seem to allow this to be of much use.

        /**
         * Register a local storage event listener.
         */
        function registerEventListener() {
            if (window.addEventListener) {
                addEventListener("storage", handleStorageEvent, false);
            }
        }

        /**
         * Handle the storage event.
         */
        function handleStorageEvent(storageEvent) {
            if (!storageEvent) {
                storageEvent = window.event;
            }
            alert("key=" + storageEvent.key +
                  " oldValue=" + storageEvent.oldValue +
                  " newValue=" + storageEvent.newValue +
                  " url=" + storageEvent.url);

        }

The entire file of examples

Here is the entire html file.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Java Ninja Local Storage prototype</title>
</head>
<body>
    <button onclick="alert(supportsHtml5Storage())">Local Storage Supported</button>
    <button onclick="alert(supportsHtml5StorageAlternate())">Local Storage Supported (alt)</button>
    <button onclick="registerEventListener()">Local Storage register listener</button>
    <button onclick="alert(getLocalStorageLength())">LocalStorage length</button>
    <button onclick="alert(displayLocalStorageContents())">LocalStorage contents</button>
    <button onclick="setLocalStorageItem()">LocalStorage set</button>
    <button onclick="alert(getLocalStorageAsString())">LocalStorage get as string</button>
    <button onclick="alert(getLocalStorageAsNumber())">LocalStorage get as number</button>
    <button onclick="removeLocalStorage()">LocalStorage remove foo</button>
    <button onclick="clearLocalStorage()">LocalStorage clear</button>

    <script>
        /**
         * Does this browser support HTML5 Local Storage.
         * @returns {boolean}
         */
        function supportsHtml5Storage() {
            try {
                return 'localStorage' in window && window['localStorage'] != null;
            } catch (e) {
                return false;
            }
        }

        /**
         * An alternative way to determine if a browser supports local storage.
         * @returns {boolean}
         */
        function supportsHtml5StorageAlternate() {
            return typeof(Storage) !== "undefined";
        }

        /**
         * Register a local storage event listener.
         */
        function registerEventListener() {
            if (window.addEventListener) {
                addEventListener("storage", handleStorageEvent, false);
            }
        }

        /**
         * Handle the storage event.
         */
        function handleStorageEvent(storageEvent) {
            if (!storageEvent) {
                storageEvent = window.event;
            }
            alert("key=" + storageEvent.key +
                  " oldValue=" + storageEvent.oldValue +
                  " newValue=" + storageEvent.newValue +
                  " url=" + storageEvent.url);

        }

        /**
         * Gets the number of items in Local Storage.
         * @returns {number}
         */
        function getLocalStorageLength() {
            return localStorage.length;
        }

        /**
         * Displays the contents of local storage.
         * @returns {string}
         */
        function displayLocalStorageContents() {
            var contents = "";
            var key = "";
            var value = "";
            for (var i = 0; i < localStorage.length; i++) {
                key = localStorage.key(i);
                value= localStorage.getItem(key);
                contents += "index=" + i + "\tkey=" + key + "\nvalue=" + value + "\n"
            }
            return contents;
        }

        /**
         * Sets a couple of items to local storage.
         */
        function setLocalStorageItem() {
            localStorage.setItem("foo", "bar");
            localStorage.setItem("max", 5);
        }

        /**
         * Gets an item from local storage.
         */
        function getLocalStorageAsString() {
            return localStorage.getItem("max") + 1;
        }

        /**
         * Gets an item from local storage.
         */
        function getLocalStorageAsNumber() {
            // local storage items are stored as a string
            return parseInt(localStorage.getItem("max")) + 1;
        }

        /**
         * Removes an item from local storage
         */
        function removeLocalStorage() {
            // removeItem does NOT return a value like Java does
            localStorage.removeItem("foo");
        }

        /**
         * Clear the contents of local storage.
         */
        function clearLocalStorage() {
            // clear did not work on FireFox 40.0.2
            localStorage.clear();
        }
    </script>
</body>
</html>

August 24th, 2015

Posted In: HTML, HTML5, java ninja, Javaninja, javascript

Tags: ,

Leave a Comment

We had a need to have a Cucumber example that tested several different steps in one pass. We then needed to make several different passes across the tests with different data elements. We had been using Examples before, but this took it to a whole new level. We defined a datatable for each step with variables that then were retrieved from a main examples datatable that had all of the data for all of the steps for this run.

The feature files looks like:

Feature: Logging in to play for real and fun money and retrieve player wallet

  @2015r16.3 @US5064
  Scenario Outline: Full register player can launch a casino game to play for real or fun money. In this scenario in the
  back end the player launch a game for real or fun (demo) the we auth the player and retrieve the wallet

    Given I am a fully registered user
    And I am authenticated
    Then I want to create an ACH account at the time of deposit with following details:
      | Routing Number             | <routing_number>   |
      | Account Nickname           | <account_nickname> |
      | Deposit Amount             | <deposit_amount>   |
      | Account Type               | <account_type>     |
      | Agree Terms And Conditions | <agree_t_and_c>    |
    Then my account balance is correct
      | Cash Balance | <deposit_amount> |
    When I get a game launch Url
      | External Game ID | <game_id_external> |
      | Aggregator ID    | <id_aggregator>    |
      | Game Mode        | <game_mode>        |
    And I am authenticated via the remote game server
      | Authorization Status | <auth_status> |
    Then the game should retrieve the wallet and verify the cash balance
      | Cash Balance | <deposit_amount> |
    Then I play the game
      | First Wager Amount        | <firstWagerAmount>       |
      | First Win Amount          | <firstWinAmount>         |
      | First Ending Cash Balance | <firstEndingCashBalance> |
    Then I play the game
      | Second Wager Amount        | <secondWagerAmount>       |
      | Second Win Amount          | <secondWinAmount>         |
      | Second Ending Cash Balance | <secondEndingCashBalance> |
    Then I cleanup the gaming records

    Examples:
      | routing_number | account_nickname | deposit_amount | account_type | agree_t_and_c | game_id_external | id_aggregator | game_mode | auth_status | firstWagerAmount | firstWinAmount | firstEndingCashBalance |
      | 011000015      | MyNewACHAccount  | 95             | chk          | true          | aris50Dragons    | gsi           | DEMO      | SUCCESS     | 10               | 0              | 85                     |
      | 011000015      | MyNewACHAccount  | 95             | chk          | true          | aris50Dragons    | gsi           | REAL      | SUCCESS     | 10               | 0              | 85                     |
      | 011000015      | MyNewACHAccount  | 95             | chk          | true          | aris50Lions      | gsi           | REAL      | SUCCESS     | 10               | 0              | 85                     |
      | 011000015      | MyNewACHAccount  | 95             | chk          | true          | aris50Lions      | gsi           | DEMO      | SUCCESS     | 10               | 0              | 85                     |

Let’s look at the step

Then I want to create an ACH account at the time of deposit with following details:

The datatable contains the following data. Think of the elements on the left as the keys in a map and the elements on the right as the corresponding values.

      | Routing Number             | <routing_number>   |
      | Account Nickname           | <account_nickname> |
      | Deposit Amount             | <deposit_amount>   |
      | Account Type               | <account_type>     |
      | Agree Terms And Conditions | <agree_t_and_c>    |

In this case, the corresponding values are within < and >. This tells Cucumber to look up the actual value in the Examples: datatable.

    Examples:
      | routing_number | account_nickname | deposit_amount | account_type | agree_t_and_c | game_id_external | id_aggregator | game_mode | auth_status | firstWagerAmount | firstWinAmount | firstEndingCashBalance |
      | 011000015      | MyNewACHAccount  | 95             | chk          | true          | aris50Dragons    | gsi           | DEMO      | SUCCESS     | 10               | 0              | 85                     |
      | 011000015      | MyNewACHAccount  | 95             | chk          | true          | aris50Dragons    | gsi           | REAL      | SUCCESS     | 10               | 0              | 85                     |
      | 011000015      | MyNewACHAccount  | 95             | chk          | true          | aris50Lions      | gsi           | REAL      | SUCCESS     | 10               | 0              | 85                     |
      | 011000015      | MyNewACHAccount  | 95             | chk          | true          | aris50Lions      | gsi           | DEMO      | SUCCESS     | 10               | 0              | 85                     |

For our examples test, we are going to send a map containing the following key/value data:

  • Routing Number -> 011000015
  • Account Nickname -> MyNewACHAccount
  • Deposit Amount -> 95
  • Account Type -> chk
  • Agree Terms and Conditions -> true

Now for the nitty gritty. This is what the step definition looks like:

    @Given("^I want to create an ACH account at the time of deposit with following details:$")
    public void I_want_to_create_an_ACH_account_at_the_time_of_deposit_with_following_details(Map<String, String> tableData) throws Throwable {
        assertTrue(tableData.containsKey("Routing Number"));
        assertTrue(tableData.containsKey("Account Nickname"));
        assertTrue(tableData.containsKey("Deposit Amount"));
        assertTrue(tableData.containsKey("Account Type"));
        assertTrue(tableData.containsKey("Agree Terms And Conditions"));


        achCashierModel.setRoute(tableData.get("Routing Number"));
        achCashierModel.setLabel(tableData.get("Account Nickname"));
        achCashierModel.setAcceptedTerms("true".equals(tableData.get("Agree Terms And Conditions")));
        achCashierModel.setIdAchType(tableData.get("Account Type"));
        achCashierModel.setTransactionAmountString(tableData.get("Deposit Amount"));

        // do some testing stuff
    }

The method takes Map tableData as the method parameter. From here on, you can access it like a regular map. In this case, the first thing the step does is verify that the correct input values were specified. To use them, a normal map.get() is called.

That’s for a single pass through the tests. Notice, that in our Examples: table, we have four rows of data. What this means is that there are 4 whole tests of data. In my feature file in my IDE, the rows of data are on lines 37, 38, 39 and 40. Here is the kind of data is produced.

Feature: CasinoGame: 28 total, 28 passed 51.51 s
    Feature: Logging in to play for real and fun money and retrieve player wallet
        Scenario Outline: Full register player can launch a casino game to play for real or fun money. In this scenario in the
            Examples:
                Scenario: Line: 37
                    Given I am a fully registered user -- passed
                    And I am authenticated -- passed
                    Then I want to create an ACH account at the time of deposit with the following details: -- passed
                    Then my account balance is correct -- passed
                    When I get a game launch Url -- passed
                    And I am authenticated via the remote game server -- passed
                    Then the game should retrieve the wallet and verify the cash balance -- passed
                Scenario: Line: 38
                    Given I am a fully registered user -- passed
                    And I am authenticated -- passed
                    Then I want to create an ACH account at the time of deposit with the following details: -- passed
                    Then my account balance is correct -- passed
                    When I get a game launch Url -- passed
                    And I am authenticated via the remote game server -- passed
                    Then the game should retrieve the wallet and verify the cash balance -- passed
                Scenario: Line: 39
                    Given I am a fully registered user -- passed
                    And I am authenticated -- passed
                    Then I want to create an ACH account at the time of deposit with the following details: -- passed
                    Then my account balance is correct -- passed
                    When I get a game launch Url -- passed
                    And I am authenticated via the remote game server -- passed
                    Then the game should retrieve the wallet and verify the cash balance -- passed
                Scenario: Line: 40
                    Given I am a fully registered user -- passed
                    And I am authenticated -- passed
                    Then I want to create an ACH account at the time of deposit with the following details: -- passed
                    Then my account balance is correct -- passed
                    When I get a game launch Url -- passed
                    And I am authenticated via the remote game server -- passed
                    Then the game should retrieve the wallet and verify the cash balance -- passed


August 21st, 2015

Posted In: Cucumber, Java, java ninja, Javaninja

Tags:

One Comment

We’ve all had this happen. There is a large model that is being converted to JSON, but only a few fields are populated. The result looks something like this

{
    "modelStatus": "ERROR",
    "message": null,
    "messages": [
    ],
    "previousPage": null,
    "nextPage": null,
    "redirect": false,
    "errors": [
    ],
    "errorFields": [
    ],
    "exception": null,
    "ssoToken": null,
    "bossUser": null,
    "idGame": 6067,
    "idAccount": 0,
    "currency": null,
    "language": null,
    "rgsCode": "aristocrat",
    "rgsGameId": "aris50Dragons",
    "tokenType": null,
    "tokenTtl": 0,
    "tokenModelAsJsonString": null,
    "channel": "pc",
    "clientType": "flash",
    "mode": "REAL",
    "clientUrl": "http://***URL with parameters removed***",
    "method": "GET",
    "contentType": null,
    "body": null,
    "status": "ERROR"
}

Jackson 2.0 (e.g. FasterXML) offers an annotation that can be used to tune the behavior of the JSON serializer. This is accomplished by placing a @JsonInclude annotation at the top of the model. There are several options.

NON_NULL can be used to tell FasterXML to not send any elements that are null.

@JsonInclude(JsonInclude.Include.NON_NULL)

This results in the same response appearing like…

{
    "modelStatus": "ERROR",
    "messages": [
    ],
    "redirect": false,
    "errors": [
    ],
    "errorFields": [
    ],
    "idGame": 6067,
    "idAccount": 0,
    "rgsCode": "aristocrat",
    "rgsGameId": "aris50Dragons",
    "tokenTtl": 0,
    "channel": "pc",
    "clientType": "flash",
    "mode": "REAL",
    "clientUrl": "http://***URL with parameters removed***",
    "method": "GET",
    "status": "ERROR"
}

Notice that there are several empty arrays. These can also be removed by specifying that only NON_EMPTY items are sent. To determine if something sould be included in the JSON, several methods are used:

  • null objects are not included
  • Collections and Maps – isEmpty() is called. The item is included if this returns false
  • Arrays – the length must be > than 0 for the array to be included
  • String – the length() is called. The string is included if the value is greater than 0.

This is accomplished by

@JsonInclude(JsonInclude.Include.NON_EMPTY)

August 21st, 2015

Posted In: Java, java ninja, Javaninja, javascript, json

Leave a Comment

Copying isn’t as straightforward as a copy or a pull request.

  1. Create the new repository – I used the GitHub to create a new repository that was available to others in my organization.
  2. Then push the source repository to the destination repository via the command line. git push –mirror https://github.com/exampleuser/new-repository.git

There you have it. You can then use the GitHub UI and you will see the files from the original project in the new project. At this point, you can clone it, etc and you have ready to code away.

August 18th, 2015

Posted In: Git, GitHub, java ninja, Javaninja

Leave a Comment

I have been using Logback for a while. It’s pretty easy to use and it’s fairly easy to configure. I noticed that I had some warning messages in my log file.

10:51:04,532 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
10:51:04,532 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
10:51:04,532 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/Users/norrisshelton/IdeaProjects/gsi-adapter/gsi-adapter-services/target/gsi-services/WEB-INF/classes/logback.xml]
10:51:04,578 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
10:51:04,586 |-INFO in ReconfigureOnChangeFilter{invocationCounter=0} - Will scan for changes in [[/Users/norrisshelton/IdeaProjects/gsi-adapter/gsi-adapter-services/target/gsi-services/WEB-INF/classes/logback.xml]] every 60 seconds. 
10:51:04,586 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - Adding ReconfigureOnChangeFilter as a turbo filter
10:51:04,589 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
10:51:04,593 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
10:51:04,649 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - This appender no longer admits a layout as a sub-component, set an encoder instead.
10:51:04,649 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.
10:51:04,649 |-WARN in ch.qos.logback.core.ConsoleAppender[STDOUT] - See also http://logback.qos.ch/codes.html#layoutInsteadOfEncoder for details
10:51:04,650 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [com.cdi] to DEBUG
10:51:04,650 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [org.springframework.web.client.RestTemplate] to DEBUG
10:51:04,650 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to WARN
10:51:04,650 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
10:51:04,651 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
10:51:04,652 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@c28ccfa - Registering current configuration as safe fallback point

I checked and discovered that the logging configuration had changed. An example is it used to look like:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{HH:mm:ss.SSS}|%-5level|${HOSTNAME}|cdi-services|%msg ||%class:%line %xException{full} %n</Pattern>
        </layout>
    </appender>

    <logger name="com.cdi" level="DEBUG"/>
    <!-- Show info on rest calls -->
    <logger name="org.springframework.web.client.RestTemplate" level="DEBUG"/>

    <root level="WARN">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

I was getting messages about layout should be wrapped in an encoder. After some tinkering, I arrived at the following:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS}|%-5level|${HOSTNAME}|cdi-services|%msg ||%class:%line %xException{full} %n</pattern>
        </encoder>
    </appender>

    <logger name="com.cdi" level="DEBUG"/>
    <!-- Show info on rest calls -->
    <logger name="org.springframework.web.client.RestTemplate" level="DEBUG"/>

    <root level="WARN">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

The gist of it is that the appender now has an encoder instead of a layout. When the correct changes are made to the configuration file, there is no longer any start-up logging from LogBack in the logs, but you will see that the new logging format is being honored.

A little note is that I leave a space after msg% to help with the wrapping when the lines are too long.

|%msg |

August 11th, 2015

Posted In: Java, java ninja, Javaninja, logback, Logging, Logging configuration

One Comment

Streams, functional programming and Lambda expressions have opened up a whole new world of syntax for the Java language. The old forEach has new competition.

An example of a normal forEach of a list of objects was:

for (MyModel myModel : myModelList) {
    System.out.println(myModel);
}

This can be replaced with .forEach and the code gets much smaller.

myModelList.forEach(System.out::println);

One common thing to do with a forEach is to iterate over a set of data and pick and choose which objects should be added to another list.
An example of a forEach that filters a list is:

List<GameModel> externalGames = new LinkedList<>();
for (GameModel myModel : gameList) {
    if ("external".equals(myModel.getIdGameExternal())) {
        externalGames.add(myModel);
    }
}

This is when it gets fun. We can use .filter and .collect to transform this into the following. The .filter contains all of the logic that you would have had in an if statement. The .collect contains the processing necessary to convert the stream to a LinkedList.

externalGames = gameList.stream()
                        .filter(myModel -> "external".equals(myModel.getIdGameExternal()))
                        .collect(Collectors.toCollection(LinkedList::new));

Sometimes you need just one element from a list. Here is what a typical forEach loop looks like that filters a list down and then we get a reference to the first object in the list.

List<GameModel> externalGames = new LinkedList<>();
for (GameModel myModel : gameList) {
    if ("external".equals(myModel.getIdGameExternal())) {
        externalGames.add(myModel);
    }
}
gameModel1 = externalGames.get(0);

We can easily do the same thing using streams. In this case, I filter like before, then grab the first element. We pull out .findFirst to retrieve the first element of the stream, then use .get to make that object available.

gameModel1 = gameList.stream()
                     .filter(myModel -> "external".equals(myModel.getIdGameExternal()))
                     .findFirst()
                     .get();

Sometimes you have the case where you want to search through a collection of data and if an element contains a specific value, set something else. In this case, we use findFirst() and isPresent() to set a boolean value based upon an element in the collection having a specific value.

inBonus = bonusRedemptionModel.getBonusTransactions()
                              .stream()
                              .filter(b -> BonusOperationEnum.OPT_OUT.value().equals(b.getIdOperation()))
                              .findFirst()
                              .isPresent();

August 6th, 2015

Posted In: Collections, Java, java ninja, Javaninja

Tags: , , , , ,

Leave a Comment

We normally use @Transactional to declaratively control transactions. Sometimes, that doesn’t quite give us the flexibility we need. I had a case where I needed the ability to write data to a database within a transaction, but I couldn’t use an exception to indicate a rollback was necessary because there was information within the method that I would need. Specifically, I needed the information related to the failure. In order for this to work. I had to use a Springframework TransactionTemplate. Like most Spring Template objects, it’s fairly easy to use. Note that an exception will cause an automatic rollback, just like the normal transactional processing.

First, you need to declare the TransactionManager and the TransactionTemplate. In this case, my transaction manager gets it’s database connection from an EntityManagerFactory because I am using JPA.

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
          p:entityManagerFactory-ref="entityManagerFactory"/>

    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"
          p:transactionManager-ref="transactionManager"/>

Once the TransactionTemplate is declared, it can be injected like any other spring bean.

    @Autowired
    private TransactionTemplate transactionTemplate;

A TransactionTemplate exposes it’s functionality via a callback method. One implementation does not have a return type and the other has a return type that can be expressed as a generic.

Here is an example of using the callback method that doesn’t have a return type. This is implemented by implementing an anonymous inner-class of type TransactionCallbackWithoutResult. This is an abstract class with one method, doInTransactionWithoutResult. The TransactionStatus object is used to rollback the transaction. This is caused by calling setRollbackOnly().

transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    @Override
    protected void doInTransactionWithoutResult(TransactionStatus status) {
        // do this
        // do that
        // if some sort of state exists
        status.setRollbackOnly();;
        // }
    }
});

If you need to return data from inside the callback method, then use the TransactionCallback. This works like the other callback method, but allows a value to be returned. Note that the Boolean return type is a generic and can be specified as whatever you need it to be.

boolean returnValue = transactionTemplate.execute(new TransactionCallback<Boolean>() {
    @Override
    public Boolean doInTransaction(TransactionStatus status) {
        // do this
        // do that
        // if some sort of state exists
        status.setRollbackOnly();
        // }
        return myObject.getMyBoolean();
    }
});

If you need a return value and you are on JDK8, you can use a Lambda expression to make the code even smaller.

boolean returnValue = transactionTemplate.execute(status -> {
    // do this
    // do that
    // if some sort of state exists
    status.setRollbackOnly();
    // }
    return null;
});

August 5th, 2015

Posted In: Java, java ninja, Javaninja, Spring

Tags: , , , , ,

Leave a Comment

LinkedIn Auto Publish Powered By : XYZScripts.com