If you're interested in functional programming, you might also want to checkout my second blog which i'm actively working on!!

Tuesday, April 15, 2014

Javascript promises demo

/**
* Check out following URL's
* http://www.html5rocks.com/en/tutorials/es6/promises/
* http://www.html5rocks.com/en/tutorials/es6/promises/#toc-async
* http://blog.parse.com/2013/01/29/whats-so-great-about-javascript-promises/
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
*
* Try this code out on http://repl.it/
*
* At present, promises are not yet supported by all browsers and some javascript libraries
* offer this functionality already.
*
* Q seems to be a popular implementation but it's performance seems not that good
* https://github.com/kriskowal/q
*
* Bleubird seems like the most promising implementation in terms of performance
* https://github.com/petkaantonov/bluebird
*
* I hope to find some time soon to prepare a demo with bluebird.
***/
function factorial(n) {
return new Promise(function(resolve, reject) {
if (n < 0) {
reject(new Error("n has to be larger than 0!!"));
} else {
var result = 1;
do {
result *= n;
n--;
}
while (n > 1);
return resolve(result);
}
});
}
function onSucces(result) {
console.log("succesfully calculated " + result);
}
function onError(error) {
console.log(error.toString());
}
factorial(5).then(onSucces, onError);
//succesfully calculated 120
factorial(-3).then(onSucces, onError);
//Error: n has to be larger than 0!!
view raw gistfile1.js hosted with ❤ by GitHub

Little Ajax exercise

Robby Pelssers 37
Valerie Pelssers 7
Lindsey Pelssers 11
view raw gistfile1.txt hosted with ❤ by GitHub
<!DOCTYPE html>
<html>
<head>
<title>Ajax demo</title>
<script data-main="scripts/ajax" src="scripts/require.js"></script>
</head>
<body>
<h1>This is a demo showing making an AJAX call<h1>
<div id="result"/>
</body>
</html>
view raw gistfile1.html hosted with ❤ by GitHub
require.config({
baseUrl: 'scripts',
paths: {
jquery: 'jquery-2.1.0.min'
}
});
requirejs(["jquery"], function($) {
/**
0 -> UNSENT
1 -> OPENED
2 -> HEADERS_RECEIVED
3 -> LOADING
4 -> DONE
**/
function handleStateChange() {
var readyState = request.readyState;
console.log(readyState);
switch(readyState) {
case 4:
processResponse(request.responseText);
break;
default:
break;
}
}
function nonEmptyString(value) {
return value !== "";
}
function processResponse(responseText) {
console.log("Processing responseText");
var lines = responseText.split('\n');
lines.filter(nonEmptyString).forEach(function(line) {
console.log("Processing line: " + line);
/**
* TODO: dynamically build a table for each line from the CSV file with JQuery
* appended as child within <div id="result"/>
**/
});
}
console.log("About to make Ajax request");
var request = new XMLHttpRequest();
request.open("GET", "data/persons.csv", true);
request.onreadystatechange = handleStateChange;
request.send();
console.log("Ajax request has been sent");
});
view raw gistfile1.js hosted with ❤ by GitHub

Javascript XPath Demo

<!DOCTYPE html>
<html>
<head>
<title>Xpath demo</title>
<script data-main="scripts/xpath" src="scripts/require.js"></script>
</head>
<body>
<h1>This is a demo showing querying the dom via Xpath<h1>
<ul>
<li>Javascript</li>
<li>Java</li>
<li>Scala</li>
<li>Ceylon</li>
<li>Python</li>
</ul>
</body>
</html>
view raw gistfile1.html hosted with ❤ by GitHub
/**
https://developer.mozilla.org/en-US/docs/Web/XPath
**/
requirejs([], function() {
//count the number of <li> tags
var listItemCount = document.evaluate( 'count(/html/body//li)', document, null, XPathResult.ANY_TYPE, null );
alert("There are " + listItemCount.numberValue + " <li> tags inside the <body> tag");
//output: There are 5 <li> tags inside the <body> tag
//stringjoin the values of the <li> nodes
var iterator = document.evaluate('/html/body//li', document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
var listItem;
var listItemValues = [];
while (listItem = iterator.iterateNext()) {
listItemValues.push(listItem.textContent);
}
alert("The languages listed are '" + listItemValues.join() + "'.");
//output: The languages listed are 'Javascript,Java,Scala,Ceylon,Python'.
});
view raw gistfile1.js hosted with ❤ by GitHub

Saturday, April 12, 2014

AngularJS demo

/**
* movies.json
**/
[
{
"title": "Avatar",
"year": 2009,
"director": "James Cameron",
"actors": ["Sam Worthington", "Sigourney Weaver"]
},
{
"title": "Aliens",
"year": 1986,
"director": "James Cameron",
"actors": ["Michael Biehn", "Sigourney Weaver"]
},
{
"title": "The Terminator",
"year": 1984,
"director": "James Cameron",
"actors": ["Michael Biehn", "Arnold Schwarzenegger", "Linda Hamilton"]
},
{
"title": "Indiana Jones and the Temple of Doom",
"year": 1984,
"director": "Steven Spielberg",
"actors": ["Harrison Ford", "Kate Capshaw"]
}
]
view raw gistfile1.js hosted with ❤ by GitHub
<!DOCTYPE html>
<html ng-app="moviesApp">
<!-- This demo only works in you serve the files from tomcat or any other webserver -->
<head>
<title>AngularJS demo</title>
<link rel="stylesheet" href="styles/movies.css" type="text/css"/>
<script data-main="scripts/movies" src="scripts/require.js"></script>
</head>
<body ng-controller="moviesController">
<h1>This page is dynamically constructed using AngularJS</h1>
<p>Total number of movies is {{movies.length}}</p>
<div>Search: <input ng-model="query"></div>
<div>Sort by:
<select ng-model="orderProp">
<option value="director">director</option>
<option value="title">title</option>
<option value="year">year</option>
</select>
</div>
<table>
<thead>
<tr>
<th>Title</th>
<th>Year</th>
<th>Director</th>
<th>Actors</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="movie in movies | filter:query | orderBy:orderProp">
<td>{{movie.title}}</td>
<td>{{movie.year}}</td>
<td>{{movie.director}}</td>
<td>{{movie.actors.join(",")}}</td>
</tr>
<tbody>
</body>
</html>
view raw gistfile1.html hosted with ❤ by GitHub
/* movies.css */
table {
border-collapse:collapse;
}
th {
text-align: left;
padding:3px;
border: 1px solid red;
}
td {
border: 1px dotted black;
padding: 3px;
}
view raw gistfile1.css hosted with ❤ by GitHub
/**
* movies.js
**/
require.config({
baseUrl: 'scripts',
paths: {
'angular': 'angular.min'
},
shim: {
"angular": {
exports: "angular"
}
}
});
requirejs(['angular'], function(angular) {
var moviesApp = angular.module('moviesApp', []);
moviesApp.controller('moviesController', function ($scope, $http) {
$http.get('data/movies.json')
.success(function(data) {
$scope.movies = data;
});
$scope.orderProp = 'title';
});
});
view raw gistfile1.js hosted with ❤ by GitHub

HTML5 Web Workers demo

<!DOCTYPE html>
<html>
<!-- This demo only works in you serve the files from tomcat or any other webserver -->
<head>
<title>Webworker demo</title>
<script data-main="scripts/app" src="scripts/require.js"></script>
</head>
<body>
<div><button id="mean">get mean</button></div>
<div><button id="max">get max</button></div>
</body>
</html>
view raw gistfile1.html hosted with ❤ by GitHub
/** data.js
// data generated using http://repl.it/languages/JavaScript
var numbers = [];
for (var i =0; i < 1000; i++) {
numbers.push(Math.round(Math.random() * 1000));
}
console.log(JSON.stringify(numbers));
**/
define({
"numbers": [375,797,951,183,732,780,599,597,156,446,156,100,58,459,866,334,601,143,708,651,21,56,970,722,832,939,212,1,182,992,183,617,304,612,525,7,432,23,291,525,612,400,139,47,292,974,366,233,456,91,785,618,200,382,514,983,592,467,46,860,608,680,171,450,65,13,949,942,966,563,808,385,305,16,98,231,684,241,440,683,122,610,495,833,34,173,909,391,259,182,663,755,312,425,520,208,547,568,185,31,970,842,775,450,939,395,895,927,598,727,922,327,88,570,196,521,45,961,325,845,389,747,271,540,829,587,357,965,281,607,543,276,141,296,802,165,75,16,987,423,772,395,199,293,6,14,815,199,707,711,729,790,771,606,74,926,358,651,116,915,863,850,623,449,331,95,64,371,311,669,325,666,730,591,638,275,887,561,472,383,120,972,713,849,761,722,561,236,771,256,494,40,523,711,428,111,25,439,108,202,31,896,636,475,314,563,509,696,908,139,249,604,410,540,756,203,229,943,77,599,290,695,161,880,930,624,808,296,633,105,871,457,804,218,187,417,893,883,539,324,807,122,896,356,318,907,110,272,228,648,427,1,818,353,861,305,7,165,511,534,417,485,222,692,120,269,338,244,943,168,323,219,519,558,703,404,364,65,972,254,962,247,252,696,497,712,301,148,285,998,37,267,610,977,503,411,51,33,279,345,908,634,240,681,145,531,489,448,986,553,242,593,672,81,762,370,238,242,728,803,368,470,632,983,634,399,536,816,90,798,835,151,321,508,187,696,41,858,591,326,678,220,17,711,512,810,226,349,645,96,174,941,691,398,387,518,937,838,138,676,341,735,113,209,925,541,877,696,258,229,660,175,817,982,555,517,530,261,242,996,93,965,897,558,900,883,633,189,339,279,349,700,726,847,897,856,887,405,780,888,642,851,84,936,162,785,899,669,606,581,9,372,101,940,664,974,5,284,161,305,549,486,692,448,652,994,224,176,712,18,237,494,325,179,746,366,650,744,849,721,658,308,568,543,94,509,368,636,265,250,244,590,973,979,393,487,892,906,631,434,795,350,503,645,577,669,493,864,195,230,722,499,281,572,24,769,645,44,177,995,940,470,954,280,915,883,370,748,15,953,928,331,428,553,967,572,964,980,853,75,294,306,385,191,851,268,317,485,169,373,557,395,936,844,696,930,570,70,97,209,615,671,990,359,140,254,518,295,877,323,741,849,697,137,702,709,359,553,294,297,809,420,810,256,867,612,913,82,511,5,502,628,798,194,650,71,702,397,796,51,890,887,338,28,376,579,94,438,578,672,36,328,466,155,543,982,287,839,591,860,31,250,37,39,823,303,360,537,127,327,522,828,770,272,216,965,623,457,85,842,52,194,531,411,541,700,637,138,726,133,976,970,516,715,323,41,795,399,271,434,439,744,78,251,25,184,963,81,836,428,696,688,409,58,173,915,156,442,250,240,549,94,715,183,660,935,280,638,955,517,738,657,554,436,612,730,420,48,248,566,356,159,758,120,14,342,116,92,46,94,41,311,855,980,704,175,474,17,98,763,492,807,473,346,173,465,434,650,399,48,616,949,635,887,45,261,375,15,626,933,503,501,856,539,659,684,163,616,71,944,642,944,27,867,586,636,940,801,575,677,388,573,643,129,458,811,546,821,941,626,386,820,961,651,905,207,196,274,69,215,101,377,18,39,94,618,683,337,71,656,319,385,845,682,23,341,814,261,282,496,118,693,697,348,629,937,877,39,735,418,803,968,282,548,177,423,751,569,807,576,991,732,413,128,372,250,776,581,341,867,931,562,858,239,429,680,751,740,755,238,103,378,903,534,505,497,826,390,320,298,896,100,389,53,11,959,905,847,91,355,319,957,950,677,951,483,573,493,632,83,448,92,293,602,329,554,673,213,752,946,792,781,790,113,91,931,494,974,58,996,550,56,442,737,888,546,351,706,117,969,143,688,762,837,618,867,101,838,84,426,701,223,73,397,822,892,706,147,81,513,85,233,987,581,374,863,371,880,813,237,947,908,986,592,753,350,376,708,84,482,777,378,558,705,424,249,906,330,111,434,493,254,11,405,469,571,56,741,119,767,118,823,649,744,746,681,583,238,962,400,375,478,286,83,869,528,224,436,963,802,12,978,970,556,43,323,891,43,528,925,993,919,74,253,554,695,969,75,523,166,629,217,696,294,455,996,628,697,584,384,901,737,45,915,281,959,950,58,890,395,456,107,620,336,277,170,188,647,464,388,353,229,584,266,78,360,974,260,986,453]
});
view raw gistfile1.js hosted with ❤ by GitHub
/** mathworker.js **/
function max(numbers) {
return Math.max.apply(Math, numbers);
}
function mean(numbers) {
var sum = numbers.reduce(function(acc, num){ return acc + num; }, 0);
return sum / numbers.length;
}
onmessage = function(event) {
var message = event.data;
var command = message.command;
var numbers = message.numbers;
switch (command) {
case "max":
postMessage("The max number is " + max(numbers));
break;
case "mean":
postMessage("The mean of these numbers is " + mean(numbers));
break;
default:
postMessage("Unknown command issued!!");
break;
}
}
view raw gistfile1.js hosted with ❤ by GitHub
require.config({
baseUrl: 'scripts',
paths: {
// the left side is the module ID,
// the right side is the path to
// the jQuery file, relative to baseUrl.
// Also, the path should NOT include
// the '.js' file extension. This example
// is using jQuery 2.1.0 located at
// scripts/jquery-2.1.0.min.js, relative to
// the HTML page.
jquery: 'jquery-2.1.0.min'
}
});
requirejs(["jquery","./data"], function($, staticdata) {
function createMathWorker() {
var worker = new Worker("scripts/mathworker.js");
worker.onmessage = function(e) {
alert(e.data);
}
return worker;
}
function getValue(event) {
var numbers = staticdata.numbers;
var command = event.data.command;
console.log("dispatching calculating " + command + " to worker");
var message = { "command": command, "numbers": numbers};
worker.postMessage(message);
}
var worker = createMathWorker();
$("#max").on("click", {"command": "max"}, getValue);
$("#mean").on("click", {"command": "mean"}, getValue);
});
view raw gistfile1.js hosted with ❤ by GitHub

Friday, April 11, 2014

Javascript event delegation (bubbling)

<!DOCTYPE html>
<html>
<head>
<title>Using event delegation</title>
<script src="scripts/matchers.js"></script>
<script src="scripts/eventdelegation.js"></script>
</head>
<body>
<div id="item-list">
<div class="item">Item 1</div>
<span class="item">Item 2</span>
<h3>Item 3</h3>
</div>
</body>
</html>
view raw gistfile1.html hosted with ❤ by GitHub
/**
* matchers.js
**/
/***********************************************************/
function Matchers(matchers) {
this.matchers = matchers || [];
}
Matchers.prototype.getMatchers = function() {
return this.matchers;
}
Matchers.prototype.add = function(matcher) {
return new Matchers(this.matchers.concat(matcher));
}
Matchers.prototype.matches = function(obj) {
return this.matchers.every(function(matcher){
return matcher.matches(obj);
});
}
Matchers.from = function() {
var matchers = [];
for (var i=0, len=arguments.length; i < len; i++) {
matchers.push(arguments[i]);
}
return new Matchers(matchers);
}
/***********************************************************/
function AbstractMatcher(predicate) {
this.predicate = predicate;
}
AbstractMatcher.prototype.matches = function(obj) {
return this.predicate(obj);
}
/***********************************************************/
ElementMatcher.prototype.constructor = ElementMatcher;
ElementMatcher.prototype = new AbstractMatcher();
function ElementMatcher(predicate) {
AbstractMatcher.prototype.constructor.call(this, predicate);
}
ElementMatcher.byClassName = function(className) {
return new ElementMatcher(
function(element) {
var classes = element.className.split(" ");
if (!classes) return false;
return classes.indexOf(className) > -1;
}
);
}
ElementMatcher.byNodeName = function(nodeName) {
return new ElementMatcher(
function(element) {
return element.nodeName === nodeName.toUpperCase();
}
);
}
/***********************************************************/
view raw gistfile1.js hosted with ❤ by GitHub
/**
* eventdelegation.js
*
* Remark: this does not work in IE as I use addEventListener which is not supported by IE.
**/
window.onload = (function(){
return function(){
//only place an onclick handler on the parent and let onclick events from the actual
//items bubble up to the parent where we handle them
document.getElementById("item-list").addEventListener("click", function(event) {
var target = event.target;
if (Matchers.from(ElementMatcher.byNodeName("div"), ElementMatcher.byClassName("item")).matches(target)) {
alert("You clicked on a <div class=\"item\"> with value '" + target.innerHTML + "'");
} else if(ElementMatcher.byClassName("item").matches(target)) {
alert("You clicked on a element with class=\"*item*\" with value '" + target.innerHTML + "'");
} else {
alert("We are not handling this click event");
}
});
};
})();
/** Compare this to JQuery
http://api.jquery.com/on/
//attach an onlick handler to each table row
$( "#dataTable tbody tr" ).on( "click", function() {
alert( $( this ).text() );
});
//attach an onclick handler to the table rows container -> tbody (use event delegation)
$( "#dataTable tbody" ).on( "click", "tr", function() {
alert( $( this ).text() );
});
**/
view raw gistfile1.js hosted with ❤ by GitHub

Thursday, April 10, 2014

Using HTML5 dataset attributes

<!DOCTYPE html>
<html>
<head>
<title>Using dataset attributes</title>
<script data-main="scripts/dataset" src="scripts/require.js"></script>
</head>
<body>
<!-- any attribute whose name is lowercase and starts with "data-" is considered
a valid HTML5 attribute -->
<span class="circle" data-radius="50" data-title="Circle of life"/>
</body>
</html>
view raw gistfile1.html hosted with ❤ by GitHub
/**
* circle.js
**/
define({
"draw": function(node) {
var dataset = node.dataset;
var radius = parseInt(dataset.radius);
var title = dataset.title;
alert("Drawing circle with title '" + title + "' and radius " + radius);
}
});
/******************************************************************************************************/
/**
* dataset.js
*
* This demo assumes my project looks like
* projectfolder
* - circle.html
* - scripts
* - dataset.js
* - circle.js
**/
requirejs(["./circle"], function(circle) {
//let's draw the circle <span class="circle" data-radius="50"/>
circle.draw(document.querySelector(".circle"));
});
view raw gistfile1.js hosted with ❤ by GitHub

Wednesday, April 9, 2014

Using requirejs as javascript module loader

<!DOCTYPE html>
<html>
<head>
<title>Demo html5 page</title>
<!-- data-main attribute tells require.js to load
scripts/app.js after require.js loads. -->
<script data-main="scripts/app" src="scripts/require.js"></script>
</head>
<body>
<h1>RequireJS demo</h1>
<div><button id="sayHi">Try me!!</button></div>
<div>languages</div>
<ul id="languages"> </ul>
</body>
</html>
view raw gistfile1.html hosted with ❤ by GitHub
/**
* data.js
**/
define({
"programminglanguages": ["Java", "Python","Scala", "Javascript"]
});
view raw gistfile1.js hosted with ❤ by GitHub
/**
* behaviour.js
**/
define({
"sayHi": function() { alert("Hi there :)"); }
});
view raw gistfile1.js hosted with ❤ by GitHub
/**
* app.js
*
* This demo assumes my project looks like
* projectfolder
* - requirejsdemo.html
* - scripts
* - app.js
* - behaviour.js
* - data.js
**/
requirejs(["./behaviour","./data"], function(behaviour, data) {
//for this little demo we will use the plain good ol' DOM API
var languagesEle = document.getElementById("languages");
data.programminglanguages.forEach(
function(language){
//first we create a <li> tag
var li = document.createElement("li");
//next we insert the language into the <li> tag
li.appendChild(document.createTextNode(language));
//we still need to attach the <li> to the existing <ul id="languages">
languagesEle.appendChild(li);
}
);
//we will also add behaviour to the <button id="sayHi">
document.getElementById("sayHi").onclick=behaviour.sayHi;
});
view raw gistfile1.js hosted with ❤ by GitHub

Friday, April 4, 2014

Ceylon: annotations

What is an annotation?

An annotation is a top level function that returns a subtype of ConstrainedAnnotation.

There are 2 interfaces which satisfy ConstrainedAnnotation directly:
  • OptionalAnnotation: An annotation that may occur at most once at a single program element and only on certain program elements.
  • SequencedAnnotation: An annotation that may occur multiple times at a single program element, and only on certain program elements.

Let's see the type hierarchy of ConstrainedAnnotation to get a better picture.


So what you might have thought to be reserved keywords (shared, formal, actual, ...) are in fact annotations used by the compiler.  Let's see how the DocAnnotation is implemented.

"The annotation class for the [[doc]] annotation."
shared final annotation class DocAnnotation(
"Documentation, in Markdown syntax, about the annotated element"
shared String description)
satisfies OptionalAnnotation<DocAnnotation, Annotated> {}
"Annotation to specify API documentation of a program
element."
shared annotation DocAnnotation doc(
"Documentation, in Markdown syntax, about the annotated element"
String description) => DocAnnotation(description);

Wednesday, April 2, 2014

Using Java's FutureTask to execute Callable tasks concurrently

In this demo we have 2 Case Providers, one for Activiti and one for WPS. I used Thread.sleep to mimic a time consuming computation (e.g. IO). Now we want to have a composite Case Provider which composes cases from both case providers. However, as we naively implement this in SequentialCaseProvider we will see that the total time it takes is the sum of having the 2 caseproviders fetch the cases (9 seconds). By using a concurrent approach in ConcurrentCaseProvider, we can reduce the time to +- the time it takes the longest CaseProvider to fetch the cases. (5 seconds)

package com.pelssers.futuretask;
public class Case {
private String message;
public Case(final String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
@Override
public String toString() {
return "Case(" + message + ")";
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.List;
public interface CaseProvider {
List<Case> getCases() throws Exception;
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.ArrayList;
import java.util.List;
public class ActivitiCaseProvider implements CaseProvider {
private static final List<Case> ACTIVITI_CASES;
static {
ACTIVITI_CASES = new ArrayList<Case>();
ACTIVITI_CASES.add(new Case("ACTIVITI CASE 1"));
ACTIVITI_CASES.add(new Case("ACTIVITI CASE 2"));
ACTIVITI_CASES.add(new Case("ACTIVITI CASE 3"));
}
public List<Case> getCases() throws Exception {
// mimic an expensive operation by sleeping for 4 seconds
Thread.sleep(4000l);
return ACTIVITI_CASES;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.ArrayList;
import java.util.List;
public class WPSCaseProvider implements CaseProvider {
private static final List<Case> WPS_CASES;
static {
WPS_CASES = new ArrayList<Case>();
WPS_CASES.add(new Case("WPS CASE 1"));
WPS_CASES.add(new Case("WPS CASE 2"));
WPS_CASES.add(new Case("WPS CASE 3"));
}
public List<Case> getCases() throws Exception {
// mimic an expensive operation by sleeping for 5 seconds
Thread.sleep(5000l);
return WPS_CASES;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.List;
public interface CompositeCaseProvider extends CaseProvider {
void setCaseProviders(final List<CaseProvider> caseProviders);
List<CaseProvider> getCaseProviders();
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.List;
public abstract class AbstractCompositeCaseProvider implements CompositeCaseProvider {
private List<CaseProvider> caseProviders;
public void setCaseProviders(final List<CaseProvider> caseProviders) {
this.caseProviders = caseProviders;
}
public List<CaseProvider> getCaseProviders() {
return caseProviders;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.ArrayList;
import java.util.List;
public class SequentialCaseProvider extends AbstractCompositeCaseProvider {
public List<Case> getCases() throws Exception {
final List<Case> cases = new ArrayList<Case>();
for (final CaseProvider caseProvider : getCaseProviders()) {
cases.addAll(caseProvider.getCases());
}
return cases;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public final class ConcurrencyHelper {
public static <T> List<T> getResults(final Iterable<Callable<T>> callables, final int numberOfThreads,
final int sleepInMillis) throws InterruptedException, ExecutionException {
final List<T> results = new ArrayList<T>();
final ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
final List<FutureTask<T>> tasks = mapToFutureTasks(callables);
for (final FutureTask<T> task : tasks) {
executor.execute(task);
}
boolean isFinished = isDone(tasks);
while (!isFinished) {
try {
Thread.sleep(sleepInMillis);
isFinished = isDone(tasks);
} catch (final InterruptedException e) {
}
}
for (final FutureTask<T> task : tasks) {
results.add(task.get());
}
executor.shutdown();
return results;
}
private static <T> List<FutureTask<T>> mapToFutureTasks(final Iterable<Callable<T>> callables) {
final List<FutureTask<T>> futureTasks = new ArrayList<FutureTask<T>>();
for (final Callable<T> callable : callables) {
futureTasks.add(new FutureTask<T>(callable));
}
return futureTasks;
}
private static <T> boolean isDone(final Iterable<FutureTask<T>> tasks) {
final boolean isDone = true;
for (final FutureTask<T> task : tasks) {
if (!task.isDone()) {
return false;
}
}
return isDone;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.List;
import java.util.concurrent.Callable;
public class CallableCaseProviderAdapter implements Callable<List<Case>> {
private CaseProvider caseProvider;
public CallableCaseProviderAdapter(final CaseProvider caseProvider) {
this.caseProvider = caseProvider;
}
public List<Case> call() throws Exception {
return caseProvider.getCases();
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
public class ConcurrentCaseProvider extends AbstractCompositeCaseProvider {
public List<Case> getCases() throws Exception {
final List<Case> result = new ArrayList<Case>();
final int numberOfThreads = 2;
final int sleepInMillis = 300;
final List<List<Case>> casesList = ConcurrencyHelper.getResults(
getCallables(), numberOfThreads, sleepInMillis);
for (final List<Case> cases : casesList) {
result.addAll(cases);
}
return result;
}
public List<Callable<List<Case>>> getCallables() {
final List<Callable<List<Case>>> callables = new ArrayList<Callable<List<Case>>>();
for (final CaseProvider caseProvider : getCaseProviders()) {
callables.add(new CallableCaseProviderAdapter(caseProvider));
}
return callables;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.futuretask;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* The output from running main():
*
* Fetching cases using com.pelssers.futuretask.SequentialCaseProvider
* [Case(ACTIVITI CASE 1), Case(ACTIVITI CASE 2), Case(ACTIVITI CASE 3), Case(WPS CASE 1), Case(WPS CASE 2), Case(WPS CASE 3)]
* Fetching cases took 9 seconds
* Fetching cases using com.pelssers.futuretask.ConcurrentCaseProvider
* [Case(ACTIVITI CASE 1), Case(ACTIVITI CASE 2), Case(ACTIVITI CASE 3), Case(WPS CASE 1), Case(WPS CASE 2), Case(WPS CASE 3)]
* Fetching cases took 5 seconds
*/
public class CaseProviderProgram {
public List<CaseProvider> getCaseProviders() {
final List<CaseProvider> caseProviders = new ArrayList<CaseProvider>();
caseProviders.add(new ActivitiCaseProvider());
caseProviders.add(new WPSCaseProvider());
return caseProviders;
}
public CaseProvider getSequentialCaseProvider() {
final CompositeCaseProvider compositeCaseProvider = new SequentialCaseProvider();
compositeCaseProvider.setCaseProviders(getCaseProviders());
return compositeCaseProvider;
}
public CaseProvider getConcurrentCaseProvider() {
final CompositeCaseProvider compositeCaseProvider = new ConcurrentCaseProvider();
compositeCaseProvider.setCaseProviders(getCaseProviders());
return compositeCaseProvider;
}
public static void main(final String[] args) throws Exception {
final CaseProviderProgram program = new CaseProviderProgram();
timeGetCases(program.getSequentialCaseProvider());
timeGetCases(program.getConcurrentCaseProvider());
}
public static void timeGetCases(final CaseProvider caseProvider) throws Exception {
System.out.println("Fetching cases using " + caseProvider.getClass().getName());
// now time how long it takes to fetch all cases using the provided caseProvider
final Date start = new Date();
final List<Case> cases = caseProvider.getCases();
System.out.println(cases);
final Date end = new Date();
final long duration = (end.getTime() - start.getTime()) / 1000;
System.out.println("Fetching cases took " + duration + " seconds");
}
}
view raw gistfile1.java hosted with ❤ by GitHub

Friday, March 28, 2014

Ceylon: building typesafe webpages with the html module

import ceylon.html { Html, html5, Head, Body, P, H2, Ul, Li }
/**
Remark: Don't forget to import the html module in your module !!
import ceylon.html "1.0.0";
Output of program:
<!DOCTYPE html>
<html>
<head>
<title>
Hello Ceylon
</title>
</head>
<body>
<h2>
Introduction
</h2>
<ul>
<li>
Basics
</li>
<li>
Classes
</li>
</ul>
</body>
</html>
**/
void htmlDemo() {
Html html = Html {
doctype = html5;
head = Head { title = "Hello Ceylon"; };
body = Body {
H2 { text = "Introduction";},
Ul {
Li {text = "Basics";},
Li {text = "Classes";}
}
};
};
print(html);
}

Ceylon: dealing with files

As we saw earlier Ceylon is modularized. So the API to deal with files is not part of the core language. Check Modularity for how to specify a dependency on Ceylon file.
import ceylon.file { ... }
/**
the parsePath function returns a Path. A path's resource either refers to an ExistingResource or Nil.
An ExistingResource is one of File|Directory|Link.
**/
void readFile() {
Path demoFilePath = parsePath("c:/tmp/demo.txt");
if (is File file = demoFilePath.resource) {
//below we define a resource expression. We don't need to take care of opening and
//closing the resource ourselves.
try (reader = file.Reader()) {
//readLine returns a String?, null if there is no more line so we
//conveniently print lines while the end of file has not been reached
while (is String line = reader.readLine()) {
print(line);
}
}
}
}
import ceylon.file { ... }
/**
In this little exercise we will write all words from a text to
a newline starting with a line number to a file:
0:I
1:like
2:programming
**/
void writeFile() {
String text = "I like programming";
Path filePath = parsePath("c:/tmp/words.txt");
//now we need to make sure the file does not already exist
if (is Nil resource = filePath.resource) {
File file = resource.createFile();
try (writer = file.Overwriter()) {
for (index -> word in entries(text.split())) {
writer.writeLine("``index``:``word``");
}
}
}
}
/**
Let's say we want to move the following folder
c:/tmp/map1 and its content
to
c:/tmp/movedmap
**/
void moveFolder() {
Path sourcePath = parsePath("c:/tmp/map1");
Path targetPath = sourcePath.siblingPath("movedmap");
if (is Directory sourceDir = sourcePath.resource) {
if (is Nil targetDir = targetPath.resource) {
sourceDir.move(targetDir);
}
}
}

Ceylon: modularity

What is modularity?
*********************
Source: Wikipedia
*********************
Modularity is the degree to which a system's components may be separated and recombined.
In software design, modularity refers to a logical partitioning of the "software design"
that allows complex software to be manageable for the purpose of implementation and maintenance.
The logic of partitioning may be based on related functions, implementation considerations,
data links, or other criteria.
***************
In practise:
***************
Let's say you have a project which has 2 immediate dependencies:
- Dependency A version 2.1 (logging)
- Dependency B version 1.4 (persistence)
BUT library B in its turn also has a dependency on A but version 1.8. So the total dependencies become
- Dependency A version 2.1 (logging)
- Dependency B version 1.4 (persistence)
- Dependency A version 1.8 (logging)
Currently all dependencies are thrown in 1 big bag and the classloader picks 1 class from that bag.
It might however pick the wrong version.
So we need a way to enable modularity, where we are guaranteed the correct version of the class is used.
For Java "Project Jigsaw" will eventually enable this kind of modularity.
For Ceylon, Modularity is at the very core of the language, SDK and tooling.
view raw gistfile1.txt hosted with ❤ by GitHub
/**
In Ceylon, you create modules and each module specifies its own dependencies.
Just like with a maven repository, modules can be shared. You can browse which Ceylon modules are available
at http://modules.ceylon-lang.org/
A nice thing is that you can even get autocompletion from within the IDE.
If you start typing "import " and press ctrl-space you get all available modules.
Below an example of what a module descriptor looks like.
**/
module pelssers.learningbyexample "1.0.0" {
import ceylon.file "1.0.0";
import ceylon.collection "1.0.0";
}

Tuesday, March 18, 2014

Ceylon: error handling the functional way

/**
Error handling (functional way)
Whenever you call a method which might result in an exception,
in Java you throw an exception which can be caught by the calling method.
But in functional programming there is a tendency to not throw an exception but return it
and make this fact explicit in the return type as well.
In Scala they came up with the Try type which represents a computation that
may result in a value of type A if it is succesful, or in some Throwable if
the computation failed.
In Ceylon there is no need for a Try type, as you can directly use a union type.
**/
shared class NegativeAgeException() extends Exception("Age can never be negative!") {}
shared class Person(shared Integer age) {
shared actual String string => "Person(``age``)";
}
shared object personBuilder extends Basic() {
shared Person | NegativeAgeException build(Integer age) {
return age > 0 then Person(age) else NegativeAgeException();
}
}
/**
main prints
pelssers.demo.NegativeAgeException "Age can never be negative!"
**/
void main() {
Person|NegativeAgeException person = personBuilder.build(-3);
//now we first MUST narrow the union type in order to use it
if (is Person person) {
print("``person`` is valid");
} else if (is NegativeAgeException person) {
print(person);
}
}

Thursday, March 13, 2014

Ceylon: creating testdata is a breeze

/**
In java it's really difficult to create complex objects for unit testing. There have been some attempts to
workaround this difficulty like using
- Object mothers --> factories to create objects (this becomes messy very quickly as well)
- Test Data builders --> http://java.dzone.com/articles/creating-objects-java-unit-tes
In Ceylon it becomes really easy to create testdata in an easy fashion as we will see.
We just make use of named arguments to construct our objects.
**/
shared class Car(engine, radio, tires) {
shared Engine engine;
shared Radio radio;
shared {Tire*} tires;
shared actual String string =>
"Car {
engine=``engine``
radio=``radio``
tires=``tires``
}";
}
shared class Engine(shared Float horsepower) {
shared actual String string => "Engine(``horsepower``)";
}
shared class Radio(shared String type) {
shared actual String string => "Radio(``type``)";
}
shared class Tire(shared String brand) {
shared actual String string => "Tire(``brand``)";
}
/**
Using the multiline string representation of Car we get a really nicely
formatted output :)
Car {
engine=Engine(150.0)
radio=Radio(FM)
tires=[Tire(Pirelli), Tire(Pirelli), Tire(Pirelli), Tire(Pirelli)]
}
**/
void demoCreatingTestData() {
Car car = Car {
engine = Engine(150.0);
radio = Radio("FM");
tires = {Tire("Pirelli"),Tire("Pirelli"),Tire("Pirelli"),Tire("Pirelli")};
};
print(car);
}

Monday, March 10, 2014

Ceylon: named arguments and declarative syntax for object creation

class Movie(String title, Director director, {Actor+} actors) {
shared actual String string => "Movie(title=``title``, Director=``director``, actors=``actors``)";
}
class Director(String name) {
shared actual String string => "Director(``name``)";
}
class Actor(String name) {
shared actual String string => "Actor(``name``)";
}
/**
demo prints
Movie(title=Avatar, Director=Director(James Cameron),
actors=[Actor(Sam Worthington), Actor(Sigourney Weaver)])
**/
void demo() {
Movie movie1 =
Movie(
"Avatar",
Director("James Cameron"),
{Actor("Sam Worthington"), Actor("Sigourney Weaver")}
);
//now we are going to construct a movie using declarative object instantiation
//As you can see this looks like a DSL language to construct objects whereas
//in fact you're just making good use of named arguments
Movie movie2 = Movie {
title = "Avatar";
director = Director("James Cameron");
actors = {Actor("Sam Worthington"), Actor("Sigourney Weaver")};
};
print(movie2);
}

Ceylon: overriding equals and hash

/**
Best practises for implementing Equals and Hash methods
Source: http://www.artima.com/lejava/articles/equality.html
------------------------------------------------------------
Common pitfalls when overriding equals:
- Defining equals with the wrong signature.
- Changing equals without also changing hashCode.
- Defining equals in terms of mutable fields.
- Failing to define equals as an equivalence relation.
The contract of the equals method in Object specifies that equals must
implement an equivalence relation on non-null objects:
It is reflexive:
for any non-null value x, the expression x.equals(x) should return true.
It is symmetric:
for any non-null values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
It is transitive:
for any non-null values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true,
then x.equals(z) should return true.
It is consistent:
for any non-null values x and y, multiple invocations of x.equals(y) should consistently return true
or consistently return false, provided no information used in equals comparisons on
the objects is modified. For any non-null value x, x.equals(null) should return false.
**/
shared abstract class Colour(shared String name) of yellow | orange | green {
shared actual Boolean equals(Object that) {
if (is Colour that) {
return that.canEqual(this) && name == that.name;
}
return false;
}
shared actual default Integer hash => name.hash;
shared default Boolean canEqual(Object that) => that is Colour;
}
shared object yellow extends Colour("yellow") {}
shared object orange extends Colour("orange") {}
shared object green extends Colour("green") {}
shared interface Coloured {
shared formal Colour colour;
}
shared class Fruit(name) {
shared String name;
shared actual default Boolean equals(Object that) {
if (is Fruit that) {
return that.canEqual(this) && name == that.name;
}
return false;
}
shared actual default Integer hash => name.hash;
shared default Boolean canEqual(Object that) => that is Fruit;
}
shared class ColouredFruit(String name, colour) extends Fruit(name) satisfies Coloured {
shared actual Colour colour;
shared actual Boolean equals(Object that) {
if (is ColouredFruit that) {
return that.canEqual(this) && name.equals(that.name) && colour.equals(that.colour);
}
return false;
}
shared actual default Integer hash => name.hash + colour.hash;
shared actual default Boolean canEqual(Object that) => that is ColouredFruit;
}
/**
comparefruits() prints
banana1 equal to banana2 = false
banana2 equal to banana1 = false
**/
void comparefruits() {
Fruit banana1 = Fruit("banana");
ColouredFruit banana2 = ColouredFruit("banana", yellow);
print("banana1 equal to banana2 = ``banana1.equals(banana2)``");
print("banana2 equal to banana1 = ``banana2.equals(banana1)``");
}

Friday, March 7, 2014

Ceylon: Range and Enumerable

/**
A quick look at the signature of Range reveals
class Range<Element>
extends Object() satisfies [Element+] & Cloneable<Range<Element>>
given Element satisfies Ordinal<Element> & Comparable<Element>
So in order to use a class in range notation we have to satisfy Ordinal & Comparable.
Let's get started and come up with an example :)
Oh yes... I used Enumerable which is a subtype of Ordinal.
**/
shared abstract class Priority(name, integerValue)
of low | middle | high | critical
satisfies Enumerable<Priority> & Comparable<Priority> {
shared String name;
shared actual Integer integerValue;
shared actual Comparison compare(Priority other) => integerValue <=> other.integerValue;
shared actual String string => name;
}
//ATTENTION !! You have to use the arrow notation for defining successor
//and predecessor, meaning it is lazy evaluated because otherwise
//you will run into a cyclic initialization error.
shared object low extends Priority("minor", 1) {
shared actual Priority predecessor => critical;
shared actual Priority successor => middle;
}
shared object middle extends Priority("middle", 2) {
shared actual Priority predecessor => low;
shared actual Priority successor => high;
}
shared object high extends Priority("high", 3) {
shared actual Priority predecessor => middle;
shared actual Priority successor => critical;
}
shared object critical extends Priority("critical", 4) {
shared actual Priority predecessor => high;
shared actual Priority successor => low;
}
/**
This prints
**************prios for range middle..critical
middle
high
critical
**************prios for range critical..middle
critical
high
middle
**************prios for range high..minor
high
middle
minor
**/
void range_demo() {
printPrios(middle..critical);
printPrios(critical..middle);
printPrios(high..low);
}
void printPrios(Range<Priority> prios) {
print("**************prios for range ``prios``");
for (prio in prios) {
print(prio);
}
}

Wednesday, March 5, 2014

Ceylon: Comparable interface and Comparison Class

/**
You can use the comparison operators
<
<=
>
>=
for all Classes satisfying the Comparable interface.
But Ceylon provides a nifty fourth operator, <=> which is the compare operator.
This results in one of the singleton instances (equal, smaller or larger) of the class Comparison.
**/
void comparison_demo() {
Boolean isFiveLargerThan2 = 5 > 2;
print("isFiveLargerThan2 is ``isFiveLargerThan2``");
Integer age = 37;
switch(age <=> 50)
case (smaller) { print("age is smaller than 50");}
case (equal) {print("age is equal to 50");}
case (larger) {print("age is larger than 50");}
//just to double check !!
Comparison comparison = age <=> 20; //compiles fine :)
}

Ceylon: Sequences and Iterables

Boolean isOdd(Integer number) => number % 2 != 0;
/**
sequence_demo prints
numbers=1..10
oddNumbers={ 1, 3, 5, 7, 9 }
doubledNumbers={ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }
sumOfNumbers=55
**/
void sequence_demo() {
Integer[] numbers = 1..10; //here we use the range shortcut notation
print("numbers=``numbers``");
Iterable<Integer> oddNumbers = numbers.filter(isOdd);
print("oddNumbers=``oddNumbers``");
Iterable<Integer> doubledNumbers = numbers.map((Integer number) => number * 2);
print("doubledNumbers=``doubledNumbers``");
Integer sumOfNumbers = numbers.fold(0, (Integer partial, Integer elem) => partial + elem);
print("sumOfNumbers=``sumOfNumbers``");
}
/**
Sequences are per definition nonempty and immutable.
Normally you would start out with the subtype Empty of Sequential (singleton instance empty)
But looking at the API of Empty we don't see any append or add methods
So how do we gradually build a sequence?
There are 2 classes which come to the rescue in this case
- SequenceBuilder (which has a no-arg constructor) => starts from empty.
- SequenceAppender (which takes a sequence as constructor parameter) => starts from non empty sequence
One should however carefully think about whether using a sequencebuilder or sequenceappender is
really necessary. Most of the times you start out from input which you just have to filter or map.
**/
class Manager(shared String name) {}
void buildSequenceDemo() {
String[] names = ["John", "Peter", "Sammy"];
Manager[] managers1 = createManagersFromNames(names);
//however, more idiomatic would have been
{Manager*} managers2 = names.map((String name) => Manager(name));
//we can also use a comprehension
{Manager*} managers3 = {for (name in names) Manager(name)};
//or if you really need a sequence instead of an iterable
Manager[] managers4 = names.map((String name) => Manager(name)).sequence;
//we can also use a comprehension
Manager[] managers5 = [for (name in names) Manager(name)];
}
Manager[] createManagersFromNames(String[] names) {
SequenceBuilder<Manager> sb = SequenceBuilder<Manager>();
for (name in names) {
sb.append(Manager(name));
}
return sb.sequence;
}
void appendDemo() {
/**
Attention: code below does not work because you have to be explicit about
names1 being a NON EMPTY sequence
**/
//String[] names1 = ["John", "Peter", "Sammy"];
[String+] names1 = ["John", "Peter", "Sammy"];
String name = "Ronald";
String[] combined = SequenceAppender(names1).append(name).sequence;
}

Ceylon: Singletons

/**
A singleton is the one and only possible instance of a Class. In languages like Java you have to use
all kinds of crazy patterns to accomplish this.
See http://en.wikipedia.org/wiki/Singleton_pattern
Not so in Ceylon however :) You just need to use an algebraic type with a single subtype (object)
**/
shared abstract class Earth() of earth {}
shared object earth extends Earth(){}
//WON'T COMPILE ==> not a subtype of any case of enumerated supertype: BabyEarth is a subtype of Earth
//class BabyEarth() extends Earth() {}
//WON'T COMPILE ==> not a subtype of any case of enumerated supertype: BabyEarth is a subtype of Earth
//shared object babyEarth extends Earth() {}

Ceylon: Higher order functions

class Coordinate(x, y) {
shared Integer x;
shared Integer y;
shared actual String string => "Coordinate(``x``,``y``)";
}
class Car(shared String name, position) {
shared variable Coordinate position;
shared void moveUp() => position = Coordinate(position.x, position.y + 1);
shared actual String string => "``name`` located at ``position``";
}
void repeat(Integer times, Anything action()) {
for (i in 1..times) {
action();
}
}
/**
this demo prints
car1 located at Coordinate(5,20)
car1 located at Coordinate(5,30)
car2 located at Coordinate(0,40)
car2 located at Coordinate(0,50)
**/
void higherorder_demo() {
Car car1 = Car("car1",Coordinate(5,20));
print(car1);
//now we can move our car 10 times up like this
for (i in 1..10) {
car1.moveUp();
}
print(car1);
//or we just call the higher order function repeat
Car car2 = Car("car2",Coordinate(0,40));
print(car2);
repeat(10, car2.moveUp);
print(car2);
}

Ceylon: Algebraic datatypes

shared interface DecisionTree<out Element, in Argument> of
Branch<Element,Argument> | Leave<Element,Argument> {
shared formal Element evaluate(Argument arg);
}
shared interface Branch<out Element,in Argument> satisfies DecisionTree<Element,Argument> {
//the predicate could be represented like this
//shared formal Callable<Boolean, [Argument]> predicate;
//but below we see the syntactic sugar declaration
shared formal Boolean(Argument) predicate;
shared formal DecisionTree<Element, Argument> left;
shared formal DecisionTree<Element, Argument> right;
shared actual Element evaluate(Argument arg) {
if (predicate(arg)) {
return left.evaluate(arg);
} else {
return right.evaluate(arg);
}
}
}
shared interface Leave<out Element, in Argument> satisfies DecisionTree<Element,Argument> {
shared formal Element element;
}
class Purchase(isMember, hasCoupon) {
shared Boolean isMember;
shared Boolean hasCoupon;
}
class PurchaseBranch(left, right, predicate) satisfies Branch<Float, Purchase> {
shared actual DecisionTree<Float,Purchase> left;
shared actual Boolean(Purchase) predicate;
shared actual DecisionTree<Float,Purchase> right;
}
class PurchaseLeave(element) satisfies Leave<Float, Purchase> {
shared actual Float element;
shared actual Float evaluate(Purchase arg) => element;
}
Boolean isMember(Purchase purchase) => purchase.isMember;
Boolean hasCoupon(Purchase purchase) => purchase.hasCoupon;
/**
isMember
|
_____________________________
| |
true false
| |
hasCoupon hasCoupon
| |
______________ ________________
| | | |
true false true false
| | | |
8 10 12 16
running our program prints
8.0
10.0
12.0
16.0
**/
void algebraic_demo() {
PurchaseBranch root = PurchaseBranch(
PurchaseBranch(
PurchaseLeave(8.0),
PurchaseLeave(10.0),
hasCoupon),
PurchaseBranch(
PurchaseLeave(12.0),
PurchaseLeave(16.0),
hasCoupon),
isMember
);
print(root.evaluate(Purchase(true, true)));
print(root.evaluate(Purchase(true, false)));
print(root.evaluate(Purchase(false, true)));
print(root.evaluate(Purchase(false, false)));
}

Ceylon: Tuples

/**
First of all, WHAT is a tuple?
In Ceylon it's represented as a typed linked list with a not so easy signature
as we will see later on.
But let's first get the picture here. Why are tuples useful. Well, sometimes you want to return 2
or more objects from a method. Now you NEED to write a wrapper class for this whereas with these
generic Tuple classes, you can just construct them like any ordinary object. You only need
to provide the correct type parameters.
In Java and Scala Tuples are modelled a bit differently. A tuple with 2 elements could look like
public class Tuple2<A, B> {
private A first;
private B second;
public Tuple2(final A first, final B second) {
this.first = first;
this.second = second;
}
public A getFirst() {
return first;
}
public B getSecond() {
return second;
}
}
Usage of such a tuple would be
Tuple2<String, Integer> nameAndAge = new Tuple2<String, Integer>("Robby", 37);
**/
/**
The tuples demo prints
Robby
Robby
37
37
<null>
**/
void tuples_demo() {
//Below we use the syntactic sugar syntax to create a tuple (Strongly recommended).
[String, Integer] tuple1 = ["Robby", 37];
/**
Below we use the fully qualified type declaration of a tuple like above
class Tuple<out Element, out First, out Rest=[]>
So you first specify the Union type of the tuple => "String | Integer"
Next you specify the type of the First element => "String"
Finally you recursively specify the same for the remainder (rest) => Tuple<Integer, Integer>
**/
Tuple<String| Integer, String, Tuple<Integer,Integer>> tuple2 = ["Davy", 37];
print(tuple1.first);
print(tuple1.get(0));//should be the same as first
print(tuple1.last);
print(tuple1.get(1)); //should be the same as last
print(tuple1.get(4)); //should print null
}

Ceylon: Interfaces and mixin inheritance

class Coordinate(x, y) {
shared Integer x;
shared Integer y;
shared actual String string => "Coordinate(``x``,``y``)";
}
/**
As you can see interfaces in Ceylon can provide concrete implementation of
methods, unlike java but comparable to Scala traits.
**/
interface Movable {
shared formal variable Coordinate position;
shared Coordinate moveUp() => position = Coordinate(position.x, position.y + 1);
shared Coordinate moveDown() => position = Coordinate(position.x, position.y - 1);
shared Coordinate moveLeft() => position = Coordinate(position.x - 1, position.y);
shared Coordinate moveRight() => position = Coordinate(position.x + 1, position.y);
}
/**
Here we define that our class MovableObject satisfies the interface Movable
We have to provide an actual position and get all the move methods for free
**/
class MovableObject(position) satisfies Movable {
shared actual variable Coordinate position;
shared actual String string => "Object located at ``position``";
}
/**
You can also provide a position when it's not passed as constructor parameter
**/
class MovableObject2(Integer x, Integer y) satisfies Movable {
shared actual variable Coordinate position = Coordinate(x,y);
shared actual String string => "Object located at ``position``";
}
/**
This demo prints the following to the console
Object located at Coordinate(5,10)
Object located at Coordinate(5,11)
Object located at Coordinate(4,11)
**/
void interface_demo() {
MovableObject movable = MovableObject(Coordinate(5,10));
print(movable);
movable.moveUp();
print(movable);
movable.moveLeft();
print(movable);
}

Tuesday, March 4, 2014

Ceylon: Optional types

/**
In case you haven't heard of optional types you might
want to take your profession more seriously :)
Yes, i'm referring to constructs like Scala's algebraic type called Option
which is either a Some(T value) or a None
Ceylon does not use a wrapper class but instead makes use of 2 new cool
features which are baked into the language "Union and Intersection Types"
together with type narrowing.
So if you want to express that a value can be either of the declared type or
null, you use the syntactic sugar notation (appending a question mark)
In order to be able to call methods on an union type, you first have to narrow it down
to a specific type. In this case we do this by testing for existence.
**/
class Person(fName, middleName, lName) {
shared String fName;
shared String? middleName; //optional
shared String lName;
shared actual String string {
//below we narrow the type to String
if (exists middleName) {
return "(Person(``fName`` ``middleName`` ``lName``))";
}
return "(Person(``fName`` ``lName``))";
}
}
/**
We can however use the else construct to provide a default value if the optional type
is indeed null. This saves a few lines of code and might be even clearer.
**/
class Person2(fName, middleName, lName) {
shared String fName;
shared String? middleName; //optional
shared String lName;
shared actual String string => "(Person(``fName`` ``middleName else ""`` ``lName``))";
}
/**
This demo prints
5
<null>
**/
void optional_demo() {
//Below we use syntactic sugar notation for optional type which basically is
//the union of types Integer and Null
Integer? optional1 = getNumber("robby");
//Below we use the fully qualified union type
Integer | Null optional2 = getNumber("john");
print(optional1);
print(optional2);
}
Integer? getNumber(String name) {
return name.startsWith("r") then 5; //else null is implicit
}

Ceylon: Introduction to classes

class ImmutablePerson1(firstName, lastName) {
shared String firstName;
shared String lastName;
//below we override the super method Object.string which has a default implementation
shared actual String string => "Person(``firstName`` ``lastName``)";
}
/**
this is shortcut syntax but this is not the preferred way when you have more than
2 class parameters (constructor parameters)
**/
class ImmutablePerson2(shared String firstName, shared String lastName) {
}
/**
We can mutate firstName and lastName of instances of MutablePerson
**/
class MutablePerson(firstName, lastName) {
shared variable String firstName;
shared variable String lastName;
shared actual String string => "Person(``firstName`` ``lastName``)";
}
/**
We can even give default values to class parameters
**/
class DefaultPerson(firstName, shared String lastName="Pelssers") {
shared String firstName;
shared actual String string => "Person(``firstName`` ``lastName``)";
}
/**
The main method prints
*******************
Person(Robby Pelssers)
Robby
*******************
com.pelssers.demo.ImmutablePerson2@4400dfe4
Davy
*******************
Person(Lindsey Pelssers)
Lindsey
*******************
Person(Valerie Pelssers)
Valerie
*******************
Person(Jef Pelssers)
Jef
*******************
**/
void main() {
print("*******************");
ImmutablePerson1 person1 = ImmutablePerson1("Robby", "Pelssers");
print(person1);
//person.firstName = "Davy"; WON'T COMPILE
print(person1.firstName);
print("*******************");
ImmutablePerson2 person2 = ImmutablePerson2("Davy", "Pelssers");
print(person2);
print(person2.firstName);
print("*******************");
MutablePerson person3 = MutablePerson("Lindsey", "Pelssers");
print(person3);
print(person3.firstName);
print("*******************");
person3.firstName = "Valerie";
print(person3);
print(person3.firstName);
print("*******************");
DefaultPerson person4 = DefaultPerson("Jef");
print(person4);
print(person4.firstName);
print("*******************");
}
/**
Below an example of how to extend other classes.
Mammal has a default implementation for eating.
Lion does not override eat() whereas
Squirrel provides it's own implementation.
**/
class Mammal(shared String name="Mammal") {
shared default void eat() {
print("``name`` eating food.");
}
}
class Lion() extends Mammal("Lion") {
}
class Squirrel() extends Mammal("Squirrel") {
//here we override the default implementation
shared actual void eat() {
print("``name`` eating nuts.");
}
}
class Cat() extends Mammal("Cat") {
//shortcut notation to refine (override) a default implementation
eat() => print("``name`` eating mouse.");
}
/**
the demo prints the following to the console:
Some mammal eating food.
Lion eating food.
Squirrel eating nuts.
Cat eating mouse.
**/
void inheritance_demo() {
Mammal mammal = Mammal("Some mammal");
mammal.eat();
Lion lion = Lion();
lion.eat();
Squirrel squirrel = Squirrel();
squirrel.eat();
Cat cat = Cat();
cat.eat();
}
abstract class Shape(name) {
shared String name;
shared formal Float circumference();
shared formal Float area();
shared actual String string => "``name`` with circumference=``circumference()``
and area=``area()``".normalized;
}
class Rectangle(Float width, Float length) extends Shape("Rectangle") {
circumference() => 2 * width + 2 * length;
area() => width * length;
}
/**
Running this program prints :
Rectangle with circumference=70.0 and area=300.0
**/
void abstractclass_demo() {
Rectangle rect = Rectangle(15.0, 20.0);
print(rect);
}

Friday, February 28, 2014

Java, Immutability and Builders

When you have to work with complex nested object structures and you want an easy way to create them and test them, I think it's definitely worthwhile to create a builder. A builder is a fluent DSL to create (domain) objects. Below you can see how that works out
package com.pelssers.domain;
public class Person {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.domain;
public class PersonBuilder {
private String firstName;
private String lastName;
/**
* @param person the source person
* @return a new personbuilder object with all properties from person copied
*/
public static PersonBuilder from(Person person) {
return
new PersonBuilder()
.withFirstName(person.getFirstName())
.withLastName(person.getLastName());
}
public Person build() {
Person person = new Person();
person.setFirstName(firstName);
person.setLastName(lastName);
return person;
}
public PersonBuilder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public PersonBuilder withLastName(String lastName) {
this.lastName = lastName;
return this;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.domain;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class PersonBuilderTest {
private static final String PELSSERS = "Pelssers";
private static final String ROBBY = "Robby";
private static final String DAVY = "Davy";
private static final String WILLIAMS = "Williams";
private Person robby;
private Person davy;
@Before
public void setUp() {
robby =
new PersonBuilder()
.withFirstName(ROBBY)
.withLastName(PELSSERS).build();
davy =
PersonBuilder
.from(robby)
.withFirstName(DAVY).build();
}
@Test
public void testRobby() {
Assert.assertEquals(PELSSERS, robby.getLastName());
Assert.assertEquals(ROBBY, robby.getFirstName());
}
@Test
public void testDavy() {
Assert.assertEquals(PELSSERS, davy.getLastName());
Assert.assertEquals(DAVY, davy.getFirstName());
}
@Test
public void testFromCreatesNewObject() {
//davy is build with a builder created from robby
//now check if we change robby's lastname this does not affect davy
robby.setLastName(WILLIAMS);
Assert.assertEquals(WILLIAMS, robby.getLastName());
Assert.assertEquals(PELSSERS, davy.getLastName());
}
}
view raw gistfile1.java hosted with ❤ by GitHub

Wednesday, January 29, 2014

Code puzzler: Tape Equilibrium

Suppose you get an array of ints of arbitrary length. Now we need to find out what is the mimimal absolute difference of the 2 sums of numbers if you split the array at any position in a left and right array. And preferably in a efficient way.
import java.util.ArrayList;
import java.util.List;
public class TapeEquilibrium {
public static void main(String[] args) {
int [] numbers1 = {1,3,2,7};
int [] numbers2 = {5,2,8,4};
evaluate(numbers1, 1);
evaluate(numbers2, 5);
}
public static void evaluate(int [] numbers, int expected) {
int answer = solution(numbers);
System.out.println("Expected answer = " + expected + ", got " + answer);
}
public static int solution(int[] A) {
List<Integer> numbers = toList(A);
int size = numbers.size();
int sumLeft = numbers.get(0);
int sumRight = sum(numbers.subList(1, size));
int minDifference = Math.abs(sumLeft - sumRight);
for (int i : numbers.subList(1, size -1)) {
sumLeft = sumLeft + i;
sumRight = sumRight - i;
int difference = Math.abs(sumLeft - sumRight);
if (difference < minDifference) {
minDifference = difference;
}
}
return minDifference;
}
public static List<Integer> toList(int [] array) {
List<Integer> result = new ArrayList<Integer>();
for (int ele : array) {
result.add(new Integer(ele));
}
return result;
}
public static int sum(List<Integer> numbers) {
int sumRight = 0;
for (int i : numbers) {
sumRight += i;
}
return sumRight;
}
}
view raw gistfile1.java hosted with ❤ by GitHub

Friday, January 10, 2014

AxonFramework - initial exploration

For the record, this demo is kind of copied from the quickstart but it is in some ways more elaborate and using the latest and greatest version 2.0.7
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.pelssers</groupId>
<artifactId>axondemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<spring.version>3.1.2.RELEASE</spring.version>
<axon.version>2.0.7</axon.version>
<junit.version>4.11</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-core</artifactId>
<version>${axon.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-test</artifactId>
<version>${axon.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
view raw gistfile1.xml hosted with ❤ by GitHub
Now we start thinking about the behaviour of our little demo application. A user can create a new todo item and he can mark an existing item as completed.
package nl.pelssers.axondemo.api;
import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier;
public class CreateToDoItemCommand {
@TargetAggregateIdentifier
private final String todoId;
private final String description;
public CreateToDoItemCommand(String todoId, String description) {
this.todoId = todoId;
this.description = description;
}
public String getTodoId() {
return todoId;
}
public String getDescription() {
return description;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package nl.pelssers.axondemo.api;
import org.axonframework.commandhandling.annotation.TargetAggregateIdentifier;
public class MarkCompletedCommand {
@TargetAggregateIdentifier
private String todoId;
public MarkCompletedCommand(String todoId) {
this.todoId = todoId;
}
public String getTodoId() {
return todoId;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
When commands are issued, something is bound to happen, so we also need to think about what events occur.
package nl.pelssers.axondemo.api;
public class ToDoItemCreatedEvent {
private final String todoId;
private final String description;
public ToDoItemCreatedEvent(String todoId, String description) {
this.todoId = todoId;
this.description = description;
}
public String getTodoId() {
return todoId;
}
public String getDescription() {
return description;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package nl.pelssers.axondemo.api;
public class ToDoItemCompletedEvent {
private final String todoId;
public ToDoItemCompletedEvent(String todoId) {
this.todoId = todoId;
}
public String getTodoId() {
return todoId;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
We also need to think about our aggregate, in this case our ToDoItem
package nl.pelssers.axondemo.api;
import org.axonframework.commandhandling.annotation.CommandHandler;
import org.axonframework.eventhandling.annotation.EventHandler;
import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot;
import org.axonframework.eventsourcing.annotation.AggregateIdentifier;
public class ToDoItem extends AbstractAnnotatedAggregateRoot<ToDoItem>{
private static final long serialVersionUID = 5438310735257852739L;
@AggregateIdentifier
private String id;
public ToDoItem() {
}
/**
* This is a constructor annotated with a CommandHandler. It makes sense that a ToDoItem
* instance gets created whenever a CreateToDoItemCommand is issued. The command handler
* creates a ToDoItemCreatedEvent which gets registered with the EventContainer by calling apply
* @param command
*/
@CommandHandler
public ToDoItem(CreateToDoItemCommand command) {
apply(new ToDoItemCreatedEvent(command.getTodoId(), command.getDescription()));
}
@EventHandler
public void on(ToDoItemCreatedEvent event) {
this.id = event.getTodoId();
}
@CommandHandler
public void markCompleted(MarkCompletedCommand command) {
apply(new ToDoItemCompletedEvent(id));
}
}
view raw gistfile1.java hosted with ❤ by GitHub
Of course we need to test if events are properly created when a command is issued.
package nl.pelssers.axondemo;
import nl.pelssers.axondemo.api.CreateToDoItemCommand;
import nl.pelssers.axondemo.api.MarkCompletedCommand;
import nl.pelssers.axondemo.api.ToDoItem;
import nl.pelssers.axondemo.api.ToDoItemCompletedEvent;
import nl.pelssers.axondemo.api.ToDoItemCreatedEvent;
import org.axonframework.test.FixtureConfiguration;
import org.axonframework.test.Fixtures;
import org.junit.Before;
import org.junit.Test;
public class ToDoItemTest {
private FixtureConfiguration<ToDoItem> fixture;
private final static String ITEM1_ID = "TODO-1";
private final static String ITEM1_DESCRIPTION = "Clean house";
@Before
public void setUp() throws Exception {
fixture = Fixtures.newGivenWhenThenFixture(ToDoItem.class);
}
@Test
public void testCreateToDoItem() throws Exception {
fixture
.given()
.when(new CreateToDoItemCommand(ITEM1_ID, ITEM1_DESCRIPTION))
.expectEvents(new ToDoItemCreatedEvent(ITEM1_ID, ITEM1_DESCRIPTION));
}
@Test
public void testMarkToDoItemAsCompleted() throws Exception {
fixture
.given(new ToDoItemCreatedEvent(ITEM1_ID, ITEM1_DESCRIPTION))
.when(new MarkCompletedCommand(ITEM1_ID))
.expectEvents(new ToDoItemCompletedEvent(ITEM1_ID));
}
}
view raw gistfile1.java hosted with ❤ by GitHub
We also need to configure the application, as usual we will use Spring to wire our app.
package nl.pelssers.axondemo.configuration;
import java.io.File;
import nl.pelssers.axondemo.ToDoEventHandler;
import nl.pelssers.axondemo.api.ToDoItem;
import org.axonframework.commandhandling.CommandBus;
import org.axonframework.commandhandling.SimpleCommandBus;
import org.axonframework.commandhandling.annotation.AggregateAnnotationCommandHandler;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.axonframework.commandhandling.gateway.DefaultCommandGateway;
import org.axonframework.eventhandling.EventBus;
import org.axonframework.eventhandling.SimpleEventBus;
import org.axonframework.eventhandling.annotation.AnnotationEventListenerAdapter;
import org.axonframework.eventsourcing.EventSourcingRepository;
import org.axonframework.eventstore.EventStore;
import org.axonframework.eventstore.fs.FileSystemEventStore;
import org.axonframework.eventstore.fs.SimpleEventFileResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AxonDemoConfiguration {
@Bean
public CommandBus commandBus() {
return new SimpleCommandBus();
}
@Bean
public CommandGateway commandGateway() {
return new DefaultCommandGateway(commandBus());
}
@Bean
public EventStore eventStore() {
return new FileSystemEventStore(new SimpleEventFileResolver(new File("c:/tmp/axondemo")));
}
@Bean
public EventBus eventBus() {
return new SimpleEventBus();
}
@Bean
public EventSourcingRepository<ToDoItem> todoRepository() {
EventSourcingRepository<ToDoItem> repository = new EventSourcingRepository<ToDoItem>(ToDoItem.class, eventStore());
repository.setEventBus(eventBus());
return repository;
}
@SuppressWarnings("unchecked")
@Bean
public AggregateAnnotationCommandHandler<ToDoItem> toDoItemHandler() {
return AggregateAnnotationCommandHandler.subscribe(ToDoItem.class, todoRepository(), commandBus());
}
@Bean
public AnnotationEventListenerAdapter todoEventHandler() {
return AnnotationEventListenerAdapter.subscribe(new ToDoEventHandler(), eventBus());
}
}
view raw gistfile1.java hosted with ❤ by GitHub
The demo app just issues 2 commands, mimicking what a user might do. And to make it interesting we also create an event handler for our ToDoItem events.
package nl.pelssers.axondemo;
import nl.pelssers.axondemo.api.CreateToDoItemCommand;
import nl.pelssers.axondemo.api.MarkCompletedCommand;
import nl.pelssers.axondemo.configuration.AxonDemoConfiguration;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class ToDoApplication {
private CommandGateway commandGateway;
public ToDoApplication(CommandGateway commandGateway) {
this.commandGateway = commandGateway;
}
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AxonDemoConfiguration.class);
ToDoApplication todoApplication = new ToDoApplication(context.getBean(CommandGateway.class));
todoApplication.run();
}
private void run() {
final String itemId = "TODO-1";
commandGateway.send(new CreateToDoItemCommand(itemId, "Clean house"));
commandGateway.send(new MarkCompletedCommand(itemId));
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package nl.pelssers.axondemo;
import org.axonframework.eventhandling.annotation.EventHandler;
import nl.pelssers.axondemo.api.ToDoItemCompletedEvent;
import nl.pelssers.axondemo.api.ToDoItemCreatedEvent;
public class ToDoEventHandler {
@EventHandler
public void handle(ToDoItemCreatedEvent event) {
System.out.println("We've got something to do: " + event.getDescription() + " (" + event.getTodoId() + ")");
}
@EventHandler
public void handle(ToDoItemCompletedEvent event) {
System.out.println("We've completed a task: " + event.getTodoId());
}
}
view raw gistfile1.java hosted with ❤ by GitHub
When we run the ToDoApplication we notice a file gets created in c:/tmp/axondemo/ for our aggregate called TODO-1.events. Also we nicely see some output appearing in the console:
We've got something to do: Clean house (TODO-1)
We've completed a task: TODO-1

Friday, January 3, 2014

Caching - the pitfall

At a current project we added some caches to our application to improve performance. However, we faced some issues I looked into and this article will demonstrate you should use caches with great precaution.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.pelssers</groupId>
<artifactId>cachingdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<spring.version>3.1.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.4.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>14.0.1</version>
</dependency>
</dependencies>
</project>
view raw gistfile1.xml hosted with ❤ by GitHub
package com.pelssers.services;
import java.util.List;
public interface PricingService {
List<Integer> getPriceHistory(Long productId);
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.services;
import java.util.List;
import org.springframework.cache.annotation.Cacheable;
import com.google.common.collect.Lists;
public class PricingServiceImpl implements PricingService {
@Cacheable(value = "services.pricing", key = "#productId")
public List<Integer> getPriceHistory(Long productId) {
System.out.println("Fetching prices from DB");
return Lists.newArrayList(2, 6, 8, 5, 9);
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.configuration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import com.pelssers.services.PricingService;
import com.pelssers.services.PricingServiceImpl;
@Configuration
@EnableCaching
public class DemoConfiguration {
@Bean
public PricingService pricingService() {
return new PricingServiceImpl();
}
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return ehCacheManagerFactoryBean;
}
@Bean
public CacheManager cacheManager() {
EhCacheCacheManager cacheManager = new EhCacheCacheManager();
cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject());
return cacheManager;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.demo;
import java.util.Collections;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import com.pelssers.configuration.DemoConfiguration;
import com.pelssers.services.PricingService;
@Component
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(DemoConfiguration.class);
PricingService pricingService = context.getBean(PricingService.class);
List<Integer> prices1 = pricingService.getPriceHistory(1L);
System.out.println(prices1);
Collections.reverse(prices1);
List<Integer> prices2 = pricingService.getPriceHistory(1L);
System.out.println(prices2);
}
}
view raw gistfile1.java hosted with ❤ by GitHub
<ehcache>
<diskStore path="java.io.tmpdir" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="true" />
<cache
name="services.pricing"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off"
statistics="true" />
</ehcache>
<!-- running Main will result in following output.
Fetching prices from DB
[2, 6, 8, 5, 9]
[9, 5, 8, 6, 2]
What do we see here... we only fetch the list of prices the first time.
The second time we get back the cached value.
BUT we actually modified the list of prices (even in our cache). Potential PITFALL !!
-->
view raw gistfile1.xml hosted with ❤ by GitHub
<ehcache>
<diskStore path="java.io.tmpdir" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="true" />
<cache
name="services.pricing"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off"
statistics="true" copyOnWrite="true"
copyOnRead="true" />
</ehcache>
<!-- running Main will result in following output.
Fetching prices from DB
[2, 6, 8, 5, 9]
[2, 6, 8, 5, 9]
What do we see here... we only fetch the list of prices the first time.
The second time we get back the cached value. And even although we reversed
the prices the first time it has NO effect on our cached value.
This is in most situations what you want.
-->
view raw gistfile1.xml hosted with ❤ by GitHub