Installing Windows 10 on a M.2 Drive

20 July 2020

When you want to install Windows 10 on a M.2 NVME drive, you have to make sure you download the correct Windows installation media otherwise the installer won’t discover your M.2 drive.


When you go to the Windows 10 download page it doesn’t tell you in any way that the ISO is completely and utterly unable to install onto a M.2 drive. When you do go down this route–and I urge you not to do that–you will be presented at the beginning of the installation that you need additional drivers.

You have to use the Installation Media Creation Tool using an existing installation of Windows to create an USB installation, not the DVD/ISO.

Fortunately (NOT), Microsoft directs you to the ISO download when you try to access the Media Creation Tool page if you are on a non-Windows operating system, so you assume that the ISO Just Works™.

Don’t be like me, don’t waste 8 hours of your precious time in frustration and just create the USB media installation media.

PS. If all you have is macOS, you can try to download Virtual Box and the Microsoft Edge test virtual machine to run the Installation Media Creation Tool.

A Wicket Hot Summer

2 July 2020

Eclipse Pro Tip: Autocomplete Template for Logging

28 May 2020

In many of my classes I need to add some logging. We use SLF4J for that in our projects. But having to type

Logger<ctrl-space> log = LoggeF<ctrl-space>.getLo<ctrl-space>(MyCla<ctrl-space>.class);

every time gets annoying and tiresome. In many live coding demos folks use templates for autocompletion, so I figured to implement one for this logging.

Eclipse supports autocomplete coding templates (Preferences -> Java -> Editor -> Templates), where you can add the snippet below:

${:import(org.slf4j.Logger,org.slf4j.LoggerFactory)}private static final Logger log = LoggerFactory.getLogger(${enclosing_type}.class);

This template will automatically insert the appropriate imports and fill in the class name for the logger.

I have named this template log, so whenever I type log<ctrl-space> Eclipse will suggest the autocompletion template above.

This probably will save a couple of hours of your life.

Interview on the Techlab podcast

14 February 2020

Right before the PostgreSQL meetup Sebastiaan Mannem and myself were interviewed by Peter Paul van de Beek and Peter Brouwers for the Techlab Podcast.

This episode has 2 subjects: PostgreSQL migration and performance. The two guests of our show will present during the PostgreSQL User group meetup at which started shortly after this recording. The topic of the meetup is introduced as PG ConfEU – The Dutch talks.

You can listen to this episode here.

CDI Factory Method

21 January 2020

I’m working in a piece of software that needs a builder inside a loop inside a CDI managed bean, but the builder should be configured using CDI.

E.G. this is part of a JBatch step that processes rows from a ResultSet

public class BananaBuilder {}

public class BananaProcessor extends AbstractBatchlet {
    // ...
    public void processRow(ResultSet rs) {
        BananaBuilder bb = ...

        ..... do something with the bananabuilder

As the BananaBuilder maintains some state that should be reset on each invocation of processRow, this is not an @Inject away: the injected instance of the BananaBuilder will only be injected once in the lifetime of the BananaProcessor instance, but I need it to be fresh on each invocation.

As I invoke processRow myself, I can’t @Inject the builder as a parameter to the method, how would I obtain that particular instance?

A Google search didn’t reveal direct usable results, because factory and CDI are rather popular generic terms together.

The correct way of doing this is either through Instance<BananaBuilder> or CDI.current().select(BananaBuilder.class).get().

So if you want to get a new instance everytime you need a BananaBuilder, then you could inject an Instance into your context:

public class BananaProcessor extends AbstractBatchlet {
    Instance<BananaBuilder> builderInstances;

    public void processRow(ResultSet rs) {
        BananaBuilder bb = builderInstances.get();

        ..... do something with the bananabuilder

This will get a new BananaBuilder on each invocation of the builderInstances.get(), achieving what we wanted. Note that the BananaBuilder needs to have @Dependent scope, which is the default scope if you don’t annotate your bean with a specific scope.


Using CDI to make a new builder inside a loop is not too difficult, but it was a bit hard to discover documentation explaining this.

PostgreSQL Meetup: Converting 85% of Dutch Primary Schools from Oracle to PostgreSQL

17 January 2020

I have been invited to give my talk “CONVERTING 85% OF DUTCH PRIMARY SCHOOLS FROM ORACLE TO POSTGRESQL” at the Amsterdam PostgreSQL meetup on Thursday, 30 January.

You can register for this Meetup here.

The description of the talk:


This case study describes migrating the most used application for primary schools in the Netherlands from Oracle to PostgreSQL. The application uses a multi-tenant, single schema database (i.e. 6000 schools in a single database) and runs using a typical Java EE frontend.

You will learn about our application architecture, hardware platform, reasons for switching, migration strategies considered and the results of our migration.

Since the CFP closed one week before our actual migration we can’t reveal the results in this abstract, but the presentation will capture all the things that went wrong and well

Hope to see you there!

If you want to see the slides, you can find them here.

Converting 85% of Dutch Primary Schools from Oracle to PostgreSQL from Martijn Dashorst

The Future of Jakarta EE in the Wake of JavaEE

7 May 2019

Gentle doctors make stinking wounds – dutch proverb

In the wake of the announcement that Oracle won’t provide the javax namespace to the Jakarta EE community to allow for modifications and future development within the existing APIs, I would like to vent my opinion.

First of all, I’m grateful to Oracle for providing all the standards work, code and documentation to the Jakarta community. This was not a light endeavor, and has cost millions of real money. It is unfortunate that the namespace and JavaEE trademarks are not transferred as well, but looking at what they already have contributed, we have a net win overall.

So what now? Mark Struberg and David Blevins both have given two options:

  1. Big Bang: migrate (mostly) everything in one go (in a next Jakarta release)
  2. On a per-case basis: migrate only when necessary

There is a continuum between those two options: migrate only the most commonly used or preferred APIs (e.g. only microprofile) at first and on demand migrate the rest.

In my opinion a Big Bang release is the way forward, and the big bang should happen with Jarkarta EE 8.

Make Jakarta EE 8 an equivalent standard to JavaEE 8 with the exact same standards, and do the package rename in this release.

Why? Because of clarity, ease of migration for users and the ecosystem as a whole. The future of JavaEE is Jakarta EE, might as well make it official with the proper package names. This will delay the release of Jakarta EE 8, but I don’t think anyone was anxiously to adopt this release as the only change would be a new steward for the standards.

There are some folks that are pushing hard for microprofile as the successor of JavaEE, not Jakarta EE. I don’t agree with them. If you work in the microprofile projects it is easy to see the rest of the world as ancient, e.g. not see the forrest through the trees. An enormous amount of code depends on JavaEE and the evolution of the parts of JavaEE. We are not asking for revolution, but evolution of those components. If you want revolution, then by all means switch to microprofile or quarkus, but those are always suitable for all applications.

Other folks want a clean slate for Jakarta EE, so they can pick and choose from JavaEE for the future. This is effectively the on a per-case basis migration option.

The Java EE ecosystem is very diverse with many different customer profiles, but from my understanding even the glacial users of Java EE are moving ahead towards newer Java EE versions. (At least the dutch IRS and the various banks are migrating away from Java 6 and Java EE 5)

In my company we use a model where we follow the latest release of Wildfly almost immediately. So our production servers are now all running on Wildfly 16, and when Wildfly 17 is released, we will adopt that quickly as well. And we use the full JavaEE profile. So to say we should just start to innovate and migrate our application to Microprofile is ingenious at best.

Given our code base of around 3 million lines of Java code, we should be afraid of any change right? Well, we actually would prefer the big bang approach. A package rename is something we can manage in a week or so, would not lead to massive retesting and would land in one sprint on our production servers. From there we can build forward on the new Jakarte EE platform and continue to follow the Jakarate EE standards.

In short: please do a big bang release of Jakarta EE 8 including the package rename.

A Better HTML Autofocus Implementation

6 November 2018

So you have this login form with a username and password field. Typically you want the username to be pre-filled and the password to be empty for a password manager to fill or to type in. It would be nice if the password field receives the focus so you can start typing away and press to submit the login form.

But often the username field is also empty. And if that is the case, you want the userfield to have the focus.

Your first pick is to add the autofocus attribute to both fields:

<label for="username">Username:</label>
<input id="username" type="text" name="username" autofocus>

<label for="password">Password:</label>
<input id="password" type="password" name="password" autofocus>

But in some browsers this focuses the username field regardless of its contents, and in other browsers the password field is focussed, even though the username field is not filled.

It appears that the behavior of two fields with autofocus is not specified.

In one of my applications I solved this problem with a little JavaScript snippet that runs when the DOM is ready.


This requires JQuery to be in your page, but if you want the Vanilla JS version, the line below will do the same:

document.querySelector('input[value=""]:enabled:not([style*="display: none"]):not([style*="display:none"]):first-child').focus()

This will filter the disabled, invisible, empty fields and select the first field to put the focus on.

If you are an Apache Wicket user, you can easily add this to your page using a header item, in particular the OnDomReadyHeaderItem, in the renderHead() method of your page.

Enjoy the best autofocus behavior you could wish for.

Generate a User Manual - With Your Tests

6 July 2017

This article was published in Java Magazine, 4th edition of 2017 in Dutch. This is a augmented Google Translate version of that article.

“Do we have a manual for the users already?” Asks the product owner. As your teammates dive under their desks, you stammer something about no time, varying priorities and the endless backlog. Without success of course, so you are the volunteer to provide the 96-page Word document with 149 screenshots. Four sprints later, the application no longer looks like the screenshots and you can start all over again.

But it can be different! After all, we stepped into this field to automate time-consuming human activities, to have time left to do fun, valuable things. We also like to write software and solve complex problems.

If I tell you that you can take screenshots automatically with the right development tools, you can integrate them into a nicely formatted manual in HTML and PDF format, and you can test your application at the same time, will I have your attention?

In this article I will show you how to generate an always up-to-date manual with AsciiDoctor, Graphene, Arquillian and aShot. It solves an actual problem, it consists of a lot of programming work, it can become as complex as you want and is also fun. To illustrate this, I use a simple project: an online cheese shop.

The Cheesr Online Cheese Shop

The cheese shop ‘Cheesr’ was invented in 2005 for the book Wicket in Action, because my co-author Eelco really missed the cheese shop “De Brink” in Deventer when he emigrated to the US. Cheesr is a standard web application with two screens: selecting cheeses and placing an order. In figure 1 you see a number of pages of the manual.

Figure 1: Cheesr Manual Figure 1. Some example pages from the Cheesr Manual

The cheese shop uses Apache Maven to connect the various components of the building process. Figure 2 illustrates the steps that are taken to get from the source code to a web application, containing the manual bundled together.

Figure 2: The build process Figure 2. The phases and files that play a role in building the cheese shop and the manual

After the source code has been compiled, the tests are run. Arquillian ensures that the web application is started and controls the browser with WebDriver . Thanks to Graphene , we can describe HTML pages as Java objects and use them to perform the tests. The library aShot makes the screenshots and puts these files in the folder target / screenshots. You will learn more about these technologies later on.

In the “pre-package” phase of the construction process, just before the web application is assembled by Maven, AsciiDoctor converts the manual from AsciiDoc to HTML and PDF. Ultimately, the web application is ready and there is also the manual as a PDF and as an HTML file.

As an example, the code in Listing 1 tests that the checkout button is not available, if you do not have an item in your shopping cart yet. In addition, the test also makes the first screenshots immediately.

Listing 1. The first test of the cheese shop

public class IndexTest {
    @Deployment (testable = false)
    public static WebArchive createDeployment () {
        return CheesrDeployment.createWar ();

    private WebDriver browser;

    public void step1EmptyCart (@InitialPage GIndex index) {
        Camera.takeScreenshot (browser, "cheesr-1-home.png");
        Camera.takeScreenshot (browser, "cheesr-1-home-navigator.png", ("navigator"));

        // nothing was selected, so the checkout should not be present
        assertFalse (index.checkoutPresent ());

The annotation @RunWith(Arquillian.class) indicates that this test case should be run with Arquillian. With Arquillian you can perform integration tests, start and stop servers and deploy your application code. You can also use Arquillian to run your tests against an already running remote server, so you do not have to set it up entirely from your tests.

The createDeployment method with the @Deployment annotation tells Arquillian what needs to be deployed to the server: a WAR in our case, containing our application code.

The @Drone WebDriver browser field is an instruction for Arquillian to inject an instance of WebDriver in the test case, in order to control the browser.

The @InitialPage GIndex index parameter of the test method indicates that this test should start with the GIndex page. GIndex is a representation of the interaction possibilities of the Cheesr homepage: an implementation of the Page Object Pattern. This class includes the location (URL) to which the browser should be sent. Graphene ensures that the GIndex instance is created correctly and that the browser loads the page just before the test has started. You can immediately interact with that page, such as taking screenshots, clicking on links and asking if certain elements are present.

For now it is useful to see how the screenshots of this test are used in the text of the manual. A piece of AsciiDoc from Listing 2 is an example of this.

Listing 2. A piece of AsciiDoc from the cheese shop manual.

== Buying Cheese

When you point your browser to our store front using the URL _http://example.com_.
This will show you the page from <<cheesr-home>>.

[[cheesr-home, figure 1]]
.The home page of the Cheesr store.

You can browse the cheeses using the navigator at the bottom of the page (<<cheesr-navigator>>).

[[cheesr-navigator, figure 2]]
.The navigator to discover even more cheese!

When you have found the cheese of your liking, you can add it to your shopping cart using the _add_ link.
This will add the cheese to the cart, as shown in <<cheesr-cart>>.

The test case from Listing 1 generates screenshots with names such as “cheesr-1-home.png”. The AsciiDoc of Listing 2 picks it up in image elements such as image::cheesr-1-home.png[]. Finally the result will look like the manual of Figure 1.

AsciiDoc and AsciiDoctor

AsciiDoc is a text format for writing documents. AsciiDoc is very similar to the widely used Markdown, but offers more options: includes, table of contents, automatic numbering of sections, warnings, annotations on code samples and much more.

AsciiDoctor is a project that can process AsciiDoc files and convert into PDF, HTML, EPub and DocBook. There is a commandline tool, which you can call directly, but also plug-ins for Maven and Jekyll (a static website generator). AsciiDoctor also integrates with chart tools such as PlantUML and GraphViz. This allows you to describe UML diagrams in plain text.

AsciiDoc is an ideal format for storing in a Git repository, because it is a plain text format. You can keep your your documentation up-to-date together with the code of your project, and even include it in code reviews (did you update the manual?)

The AsciiDoc content of the sample project (in which this article is also written) looks like this:

├── artikel.adoc
├── cheesr.adoc
└── images
    ├── build-process.png
    ├── cheesr-cover.jpg
    ├── cheesr-manual.jpg
    └── pageobjects.png

With these documents AsciiDoctor can generate the manual and include the screenshots (from target/screenshots) in the manual. The screenshots are taken during the execution of the application tests. This has been set up using Arquillian, WebDriver and Graphene.

WebDriver and Graphene

WebDriver is a technology for controlling and reading browsers. This can produce fragile code if you are not paying attention: changes in the structure of the HTML in the browser can easily make your tests fail. Graphene is a shell around WebDriver to describe your application at a higher level of abstraction by encapsulating the (interaction with) HTML by enabling the Page Object pattern.

Instead of looking for a link in the HTML document to add a cheese to the shopping cart, you call the addCheese method on your page object. Of course behind the scenes, the search in the HTML goes to the link in question, but that is hidden from your tests. Figure 3 shows the difference between working directly with the WebDriver API (and with it the HTML) and working with a Page Objects API.

WebDriver API vs Page Objects Figure 3. The difference between the WebDriver API and Page Objects

The use of page objects makes your tests much easier to read. The code in Listing 3 gives an example of the difference between the WebDriver API and the use of Page Objects.

Listing 3. Example of working with the WebDriver API and Page Objects

// WebDriver code:

List <WebElement> addLinks = browser
    .findElements(By.cssSelector("a[id^=add]")); ()
    .filter(e -> e.getAttribute("id").contains("edam"))


// page objects code:


Assert.assertTrue(index.checkoutIsPresent ());

In the example of the page objects code the intention of the test is immediately clear, while that in the WebDriver code is hidden in pitting the HTML structure. Of course this is simply moving the WebDriver logic to a façade, but that is precisely the essence of the Page Object pattern.

The code in Listing 4 shows the implementation of the GIndex page object.

Listing 4. A Page Object for the Cheesr homepage

public class GIndex {
    private List <GrapheneElement> addLinks;

    private GrapheneElement checkout;

    public void addCheese(String cheese) { ()
            .filter(a -> a.getAttribute ("id").contains(safeCheeseId(cheese)))
            .orElseThrow(() -> new NoSuchElementException("Add" + cheese + "link"))

    public boolean checkoutPresent() {
        return checkout.isPresent();

    public By byCart() {
        return By.cssSelector("div[id=cart], input[type=button]");

The code starts with @Location to tell where the page is located in the application: "" means the root. From there all the links are collected, which can add a cheese to the shopping cart. Finally, the addCheese method offers the possibility to add a cheese based on the name by clicking on the corresponding link.

Thanks to Graphene, you can easily apply the Page Objects pattern to create a model of your application, so that your tests remain readable and maintainable. Now we still have to actually make the screenshots. The library aShot is made to make screenshots with WebDriver.

Screenshots with aShot

aShot has a fairly simple but powerful API. You only have to provide aShot with the WebDriver and the element you want to make the screenshot of. aShot will give you the picture. You can do some extra things and so it is useful to wrap it in a function that you can call from your tests.

You can also specify more web elements, of which a picture has to be taken. In addition, it is also possible to add extra space to the elements and you can release filters on the surrounding area, such as black-white or blur (blur) of the edge. The code in Listing 5 shows how you can achieve this with aShot.

Listing 5. Takes a screenshot of specific elements in the page

public static void takeScreenshot(WebDriver browser, String name, By crop) {
    IndentCropper cropper = new IndentCropper(25)
        .addIndentFilter(new MonochromeFilter());

    BufferedImage screenshot = new AShot()
        .takeScreenshot(browser, browser.findElements(crop))

    saveScreenshot(name, screenshot);

Making a screenshot has become very simple with this function. Now we can save screenshots during testing and use them in our manual (see Listing 6).

Listing 6. A test that uses the page object GIndex and takes screenshots

public void step2AddToEmptyCart(@InitialPage GIndex index) {
    Camera.takeScreenshot(browser, "cheesr-2-cart-0-empty.png", index.byCart());
    index.addCheese ("edam");
    Camera.takeScreenshot (browser, "cheesr-2-cart-1-edam.png", index.byCart());

    // assert that the checkout button is present

An example of the screenshots taken in this test is shown in Figure 4. ! Screenshots of listing 6 Figure 5. Two screenshots of the shopping cart

This completes the article. We can now test the application, take screenshots and generate a manual.


We have one integrated whole, because the documentation lives together with the code in AsciiDoc format. We test our application via the browser, thanks to Arquillian and Graphene. During the execution of the tests we make screenshots and focus on exactly those parts that are important with aShot. Maven combines all these steps into one smooth process, which ultimately incorporates our up-to-date manual into our application.

With this setup you can also create internationalized screenshots for multilingual manuals: put your application in another language and perform the tests again. Now only someone willing to translate that 96 pages of text to translate …

View all code on Github

The source code is available on Github: It is a Maven project, so you can import it directly into your favorite IDE. The contains instructions for building and running the project. In this article I do not dwell on the specific versions and configuration of plug-ins and dependencies. You can find this in the project.

Is Blendle the Savior of Traditional Media?

3 May 2015

Ever since we invited Alexander Klöpping to our company’s conference as a keynote speaker I have been following his endeavors. He’s a busy fellow and does some amazing stuff. He’s created the University of the Netherlands, a televised series of lectures rich in media by renowned professors. And he created a startup that is shaking up old media.


Blendle strives to revolutionize the way you consume news using micropayments in a sensible way. Blendle has contracted with several traditional newspapers and magazines (mostly in the Netherlands and Belgium), and is now increasing its reach across the atlantic ocean with content from the Wall Street Journal and the New York Times.

The idea is that you can read an article for a small amount of money–a micro-payment. So one article could set you back €0.19 and another €0.59. The price depends on the publisher, the content and demand.

Curated content combined with technology

The site features a lot of curated content and automated trending content. Of course any article you read is accompanied by a list of related articles.

Blendle’s setup wizard lets you create your own curated list of publications. The publications you have selected will be featured in a section called “My Blendle” and will list the most popular articles. You can also select categories that will form the top menu bar and the site will select the trending and popular articles for each category:

Blendle top menu bar showing categories

For example I picked “Tech”, “Big Interviews”, “Foreign”, “Science”, “Media” and “Education” as my categories, as shown in the prior image.

When you want “Share on the Interwebs” Blendle has you covered, allowing you to share links to articles through social media (Twitter, Facebook and LinkedIn), the platform itself or through email.

Engage more with the daily newsletters

You’ll recieve a curated list of articles of all morning editions in your inbox. You’ll also receive a message when a new issue of a magazine is published, and one message at the end of the week with an overview of the most important and popular articles.

Blende daily newsletter

Of course you can opt out of these messages with a familiar “Recieve too much mail from us? Click here to unsubscribe of this daily newsletter”. The preferences page on their site has several options for customizing your need to stay informed.

Each daily message ends with a nice “Psst, this is also noteworthy” suggestion for some tongue in cheek content, making the newsletter bit more entertaining. For example:

Pssssssst! Eva had an affair with a co-worker just after getting married. The affair stopped and her marriage was saved, but then Eva ran into her co-worker by chance

Of course this is click bait, but the Blende team appears to use it as a form of satire rather than a monetization strategy.

Readers can have their cake and eat it too

The platform is thought through very well: when you are already a subscriber to a participating newspaper you get all their articles for free on blendle–you already paid for them. You still get access to all other articles from other publishers, and there’s no risk of paying for an article twice.

Buy an issue or use an existing subscription with Blendle

When you read an article you can get the whole issue where it appeared in (e.g. the saturday newspaper) at a discount: the amount you already spent on the articles of that issue. While I haven’t tried it, it wouldn’t surprise me that when you buy enough articles to offset the cost of the complete issue, it unlocks the issue automatically.

Satisfaction guarantee or your money back

When you read an article but don’t like it, or the price doesn’t fit the article in your opinion, Blendle will gladly refund you. You’re only asked a simple question: why you want your money back. Blendle provides you with a list of prepared answers, or provide your own feedback for the author and publisher.

Satisfaction Guarantee or your money back

The ease of getting a refund makes it risk free for clicking on articles. The price of newspaper articles is low enough to ignore the cost and are pretty much on par what you’d want to pay. Articles from magazines (looking at you Viva) are often priced too high in my short experience with the platform.

Blendle also released an app for mobile called “Blendle Trending”. It currently only shows the curated list of trending articles, and your reading list. It was launched as a single day public beta on the App Store for iOS. The app works, but is very limited in its use when compared to the web site. I expect the app to become the major interaction vector for Blendle really soon.

Awesome design

The design of the website (and app) is gorgeous. The articles look good, are nicely typesetted using good readable fonts not unlike the original publication’s type face. In fact the website loads about 45 different type faces in multiple weights.

The gorgeous design of Blendle

The user experience is great too. The tone of voice is familiar, friendly. It might be a bit too familiar for some folks, but it didn’t bother me. As the site is aimed at the 20 something crowd I think they hit the sweet spot.


The Blendle team has created a really interesting platform for the future of publications, enticing youngsters to read background articles that were previously hidden inside old media. Who has time to read magazines, news papers and other publications in our age of 140 character snippets? Who even has time to read 140 character snippets when a couple of 😜🎓✈️ will do?

Blendle has crafted a great experience to entice not only the youngsters but also the old media. Springer and the New York Times are on board as investors and have their publications available through the site. The team has grown from 3 to 40 in a very short time and is one of the Netherlands’ most successful start ups.

I’m just a recent user of Blendle, but I’ll keep checking their site and application for more interesting news every day, consuming it a micropayment at a time.

How to keep YouTube working on your first gen iPad

26 April 2015

YouTube is shutting down version 2 of its API, effecitvely killing the support for devices that have YouTube apps that haven’t been updated to use the newer version. This includes many ‘Smart’ TVs, and of course the first iPad.

The YouTube API is used to search for, and serve videos to your devices. The version 2 of the API provided limited ad support to YouTube, which makes the site run. That combined with a declining number of users of the old API version, made YouTube decide to shut down the old version. This saves on a lot of maintenance on their part.

This decision leaves many parents without a working YouTube app on their old, passed down iPad 1s. The first iPad is stuck at iOS 5.1 and starting from iOS 6 Apple no longer provides their own YouTube app, nor will Apple update the old YouTube app to use the newer version of the YouTube API.

YouTube suggests using the mobile version of their website, but considering that the Safari browser on the first iPad is also no longer maintained and has many unfixed security issues, this is not a good path to take.

Fortunately there seems to be a workaround to install an older version of the official YouTube app on your iPad 1:

  1. purchase the official YouTube app on a supported device
  2. on your iPad 1 go to App Store » Purchased
  3. search for YouTube
  4. click the download button

Your iPad will ask you to download an older version of the app, because the current version requires iOS 6.

  1. Confirm the download of the older version
  2. $$$ profit

This will install a new YouTube app on your iPad with an updated (but older) YouTube icon (white shield with black “You” and a red button with white “Tube”).

Enjoy while it lasts!

Improve Error Reporting in Java EE

9 March 2015

While I am a great advocate of adopting standards and have come to dislike ducttaped together solutions of the years, I still have some qualms with Java (EE) in general and WildFly in particular.

My number one dislike is the error reporting during deployment. These are inscrutable, unsolvable, unhelpful messages that are complete and utter gibberish. If there should be one fricking standard that should be adopted for Java EE 8 it should be better, standardized error reporting.

Exhibit A:

14:47:37,980 ERROR [] (MSC service thread 1-15) MSC000001: Failed to start service jboss.deployment.subunit."eduarte-deliverable-dev-ear.ear"."eduarte-common-dao-2.42-SNAPSHOT.jar".PARSE: org.jboss.msc.service.StartException in service jboss.deployment.subunit."eduarte-deliverable-dev-ear.ear"."eduarte-common-dao-2.42-SNAPSHOT.jar".PARSE: JBAS018733: Failed to process phase PARSE of subdeployment "eduarte-common-dao-2.42-SNAPSHOT.jar" of deployment "eduarte-deliverable-dev-ear.ear"
	at [wildfly-server-8.1.0.Final.jar:8.1.0.Final]
	at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService( [jboss-msc-1.2.2.Final.jar:1.2.2.Final]
	at org.jboss.msc.service.ServiceControllerImpl$ [jboss-msc-1.2.2.Final.jar:1.2.2.Final]
	at java.util.concurrent.ThreadPoolExecutor.runWorker( [jrebel-bootstrap-a53fd962a83484078782af30d2cabc06.jar:1.8.0_31]
	at java.util.concurrent.ThreadPoolExecutor$ [jrebel-bootstrap-a53fd962a83484078782af30d2cabc06.jar:1.8.0_31]
	at [jrebel-bootstrap-a53fd962a83484078782af30d2cabc06.jar:]
Caused by: org.jboss.msc.service.ServiceNotFoundException: Service service jboss.ejb.default-resource-adapter-name-service not found
	at org.jboss.msc.service.ServiceContainerImpl.getRequiredService( [jboss-msc-1.2.2.Final.jar:1.2.2.Final]
	at [wildfly-server-8.1.0.Final.jar:8.1.0.Final]
	... 5 more

And a couple of hundred lines below:

14:47:42,608 ERROR [] (Controller Boot Thread) JBAS014613: Operation ("deploy") failed - address: ([("deployment" => "eduarte-deliverable-dev-ear.ear")]) - failure description: {"JBAS014671: Failed services" => {"jboss.deployment.subunit.\"eduarte-deliverable-dev-ear.ear\".\"eduarte-common-dao-2.42-SNAPSHOT.jar\".PARSE" => "org.jboss.msc.service.StartException in service jboss.deployment.subunit.\"eduarte-deliverable-dev-ear.ear\".\"eduarte-common-dao-2.42-SNAPSHOT.jar\".PARSE: JBAS018733: Failed to process phase PARSE of subdeployment \"eduarte-common-dao-2.42-SNAPSHOT.jar\" of deployment \"eduarte-deliverable-dev-ear.ear\"
    Caused by: org.jboss.msc.service.ServiceNotFoundException: Service service jboss.ejb.default-resource-adapter-name-service not found"}}
14:47:42,642 INFO  [] (ServerService Thread Pool -- 28) JBAS018559: Deployed "eduarte-portal-eo-student.war" (runtime-name : "eduarte-portal-eo-student.war")
14:47:42,642 INFO  [] (ServerService Thread Pool -- 28) JBAS018559: Deployed "eduarte-portal-eo-ouder.war" (runtime-name : "eduarte-portal-eo-ouder.war")
14:47:42,642 INFO  [] (ServerService Thread Pool -- 28) JBAS018559: Deployed "eduarte-portal-eo-docent.war" (runtime-name : "eduarte-portal-eo-docent.war")
14:47:42,642 INFO  [] (ServerService Thread Pool -- 28) JBAS018559: Deployed "eduarte-portal-eo-bedrijf.war" (runtime-name : "eduarte-portal-eo-bedrijf.war")
14:47:42,643 INFO  [] (ServerService Thread Pool -- 28) JBAS018559: Deployed "eduarte-portal-eo-authenticator.war" (runtime-name : "eduarte-portal-eo-authenticator.war")
14:47:42,643 INFO  [] (ServerService Thread Pool -- 28) JBAS018559: Deployed "eduarte-deliverable-dev-ear.ear" (runtime-name : "eduarte-deliverable-dev-ear.ear")
14:47:42,644 INFO  [] (Controller Boot Thread) JBAS014774: Service status report
JBAS014777:   Services which failed to start:      service jboss.deployment.subunit."eduarte-deliverable-dev-ear.ear"."eduarte-common-dao-2.42-SNAPSHOT.jar".PARSE: org.jboss.msc.service.StartException in service jboss.deployment.subunit."eduarte-deliverable-dev-ear.ear"."eduarte-common-dao-2.42-SNAPSHOT.jar".PARSE: JBAS018733: Failed to process phase PARSE of subdeployment "eduarte-common-dao-2.42-SNAPSHOT.jar" of deployment "eduarte-deliverable-dev-ear.ear"

14:47:42,653 INFO  [] (Controller Boot Thread) JBAS015961: Http management interface listening on
14:47:42,653 INFO  [] (Controller Boot Thread) JBAS015951: Admin console listening on
14:47:42,654 ERROR [] (Controller Boot Thread) JBAS015875: WildFly 8.1.0.Final "Kenny" started (with errors) in 22967ms - Started 1208 of 1315 services (4 services failed or missing dependencies, 225 services are lazy, passive or on-demand)
14:47:42,936 INFO  [] (MSC service thread 1-13) JBAS015974: Stopped subdeployment (runtime-name: eduarte-common-dao-2.42-SNAPSHOT.jar) in 38ms
14:47:42,942 INFO  [] (MSC service thread 1-7) JBAS015974: Stopped subdeployment (runtime-name: eduarte-ws-eo-rest-2.42-SNAPSHOT.war) in 44ms
14:47:43,054 INFO  [] (MSC service thread 1-6) JBAS015974: Stopped subdeployment (runtime-name: eduarte-sis-web-main.war) in 156ms
14:47:43,088 INFO  [] (MSC service thread 1-9) JBAS015877: Stopped deployment eduarte-deliverable-dev-ear.ear (runtime-name: eduarte-deliverable-dev-ear.ear) in 195ms
14:47:43,136 INFO  [] (DeploymentScanner-threads - 2) JBAS018558: Undeployed "eduarte-deliverable-dev-ear.ear" (runtime-name: "eduarte-deliverable-dev-ear.ear")
14:47:43,137 INFO  [] (DeploymentScanner-threads - 2) JBAS014774: Service status report
JBAS014777:   Services which failed to start:      service jboss.deployment.subunit."eduarte-deliverable-dev-ear.ear"."eduarte-common-dao-2.42-SNAPSHOT.jar".PARSE

14:47:47,715 WARN  [] (DeploymentScanner-threads - 2) JBAS015002: Deployment of 'eduarte-web-main.war' requested, but the deployment is not present
14:47:47,715 INFO  [] (DeploymentScanner-threads - 2) JBAS015003: Found eduarte-deliverable-dev-ear.ear in deployment directory. To trigger deployment create a file called eduarte-deliverable-dev-ear.ear.dodeploy

Apparently something is missing, but the stack traces don’t contain any code that is under my purview, and the error message is really clear and helpful in identifying what is missing:

MSC000001: Failed to start service jboss.deployment.subunit.”eduarte-deliverable-dev-ear.ear”.”eduarte-common-dao-2.42-SNAPSHOT.jar”. PARSE: org.jboss.msc.service.StartException in service jboss.deployment.subunit.”eduarte-deliverable-dev-ear.ear”.”eduarte-common-dao-2.42-SNAPSHOT.jar”. PARSE: JBAS018733: Failed to process phase PARSE of subdeployment “eduarte-common-dao-2.42-SNAPSHOT.jar” of deployment “eduarte-deliverable-dev-ear.ear”

Which is caused by:

org.jboss.msc.service.ServiceNotFoundException: Service service jboss.ejb.default-resource-adapter-name-service not found

What the hell is Wildfly trying to tell me?

The solution (of course) is to run the configuration script that is external to our application, in order to tell Wildfly that a new message queue should be added:

15:03:30,952 WARN  [org.jboss.messaging] (management-handler-thread - 2) JBAS011618: There is no resource matching the expiry-address jms.queue.ExpiryQueue for the address-settings #, expired messages from destinations matching this address-setting will be lost!
15:03:30,952 WARN  [org.jboss.messaging] (management-handler-thread - 2) JBAS011619: There is no resource matching the dead-letter-address jms.queue.DLQ for the address-settings #, undelivered messages from destinations matching this address-setting will be lost!
15:03:31,033 WARN  [] (MSC service thread 1-16) JBAS011600: AIO wasn't located on this platform, it will fall back to using pure Java NIO. If your platform is Linux, install LibAIO to enable the AIO journal
15:03:31,149 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221000: live server is starting with configuration HornetQ Configuration (clustered=false,backup=false,sharedStore=true,journalDirectory=/Users/dashorst/Workspaces/luna/wildfly-8.1.0.Final/standalone/data/messagingjournal,bindingsDirectory=/Users/dashorst/Workspaces/luna/wildfly-8.1.0.Final/standalone/data/messagingbindings,largeMessagesDirectory=/Users/dashorst/Workspaces/luna/wildfly-8.1.0.Final/standalone/data/messaginglargemessages,pagingDirectory=/Users/dashorst/Workspaces/luna/wildfly-8.1.0.Final/standalone/data/messagingpaging)
15:03:31,151 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221006: Waiting to obtain live lock
15:03:31,222 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221013: Using NIO Journal
15:03:31,288 INFO  [io.netty.util.internal.PlatformDependent] (ServerService Thread Pool -- 72) Your platform does not provide complete low-level API for accessing direct buffers reliably. Unless explicitly requested, heap buffer will always be preferred to avoid potential system unstability.
15:03:31,359 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221043: Adding protocol support CORE
15:03:31,372 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221043: Adding protocol support AMQP
15:03:31,380 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221043: Adding protocol support STOMP
15:03:31,427 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221034: Waiting to obtain live lock
15:03:31,428 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221035: Live Server Obtained live lock
15:03:31,610 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221007: Server is now live
15:03:31,610 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 72) HQ221001: HornetQ Server version 2.4.1.Final (Fast Hornet, 124) [1141831d-c665-11e4-86db-0ffb92fb33f7] 
15:03:31,666 INFO  [] (ServerService Thread Pool -- 72) JBAS011601: Bound messaging object to jndi name java:/messaging/ConnectionFactory
15:03:31,672 INFO  [org.hornetq.core.server] (ServerService Thread Pool -- 73) HQ221003: trying to deploy queue jms.queue.eventQueue
15:03:31,785 INFO  [] (ServerService Thread Pool -- 73) JBAS011601: Bound messaging object to jndi name queue/eventQueue
15:03:31,820 INFO  [] (MSC service thread 1-10) JBAS010406: Registered connection factory java:/messaging/JmsXA
15:03:31,908 INFO  [org.hornetq.ra] (MSC service thread 1-10) HornetQ resource adaptor started
15:03:31,908 INFO  [$ResourceAdapterActivator] (MSC service thread 1-10) IJ020002: Deployed: file://RaActivatorhornetq-ra
15:03:31,910 INFO  [] (MSC service thread 1-5) JBAS010401: Bound JCA ConnectionFactory [java:/messaging/JmsXA]
15:03:31,910 INFO  [] (MSC service thread 1-16) JBAS011601: Bound messaging object to jndi name java:jboss/DefaultJMSConnectionFactory

Unfortunately the original error message did not provide any useful insights as to what was missing.

20 February 2015

A list of all active space probe missions including mission details.

New Horizons Rosetta
The New Horizons and Rosetta missions

Via @kottke

Brianna Wu Risking Life versus Gamergate

20 February 2015

Unreal what happens to Brianna Wu. I admire her will to continue to fight the good fight and I hope she’ll be safe. Nobody deserves what she has to go through. This is from 9 days ago:

This weekend, a man wearing a skull mask posted a video on YouTube outlining his plans to murder me. I know his real name. I documented it and sent it to law enforcement, praying something is finally done. I have received these death threats and 43 others in the last five months.

Read the whole article and be appalled at the threats and vitriol she recieves for being a woman in tech.

IBM Design Language

10 February 2015

Who’d thought: IBM talking design.

“Living Language – A shared vocabulary for design”

Via Daring Fireball

Designing a Backend for a Famous Backend

10 February 2015

When Kim Kardashian and Paper magazine set out to break the internet, it was the task of one engineer to make sure that Paper’s backend systems wouldn’t crumble under the weight of Kim’s backend.

Hosting that butt is an impressive feat. You can’t just put Kim Kardashian nudes on the Internet and walk away —that would be like putting up a tent in the middle of a hurricane. Your web server would melt. You need to plan.

And the understatement of the year:

“We may get a lot of traffic.”

Rather interesting read how to scale a system with mostly static content, but that will get hammered by millions within a few hours.

I'm an Anti-Braker

10 February 2015

A brilliant piece by Robert Moore:

A few weeks ago I saw a car accident - two people went through an intersection at the same time. Both slammed on their brakes at the same time and collided. Fortunately no one was seriously injured.

But then it occurred to me - if they had just gone through the intersection, they wouldn’t have collided. The brakes CAUSED the accident!

Maven incremental builds

6 February 2015

One of the issues we have at our company is that build times for our major products take a long time to complete, hindering adoption of non-incremental build IDEs such as Netbeans.

A possible way out is to adopt takari’s incremental build features. This is certainly something we are going to try out in the coming week(s).

From the Takari Lifecycle as a replacement for Maven’s default lifecycle page:

TEAM includes an optimized replacement for the Maven default lifecycle. The Takari Lifecycle Plugin provides you access to a number of significant advantages:

  1. One plugin with a small set of dependencies provides equivalent functionality to five plugins with a large set of transitive dependencies. This reduces the download times to retrieve the needed components as well as the storage space requirements in your repositories.

  2. The configuration for a number of aspects for your build is centralized to one plugin and simplified.

  3. The reduced complexity of the plugins involved in the build, results in higher build performance on the command line and in the IDE.

  4. The build is fully incremental, not only for your source code, but also for your resources, which in turn again speeds up development cycle and build times.

  5. Dedicated IDE support brings the advantages of the lifecyle to your daily development work.

The AI Revolution: The Road to Superintelligence

6 February 2015

In the category of interesting things to read when you have some time, this first part of a series by Tim Urban should be on your list:

It’s most intuitive for us to think linearly, when we should be thinking exponentially. If someone is being more clever about it, they might predict the advances of the next 30 years not by looking at the previous 30 years, but by taking the current rate of progress and judging based on that.


And everything we just mentioned is still only taking in stagnant information and processing it. To be human-level intelligent, a computer would have to understand things like the difference between subtle facial expressions, the distinction between being pleased, relieved, content, satisfied, and glad, and why Braveheart was great but The Patriot was terrible.

Read it all. Part 2 is also available.

Missing the Point of Server-Side Rendered JavaScript Apps

6 February 2015

The death of server-side rendering has been greatly exaggerated. Ember.js developer Tom Dale:

Say what you will about server-rendered apps, the performance of your server is much more predictable, and more easily upgraded, than the many, many different device configurations of your users. Server-rendering is important to ensure that users who are not on the latest-and-greatest can see your content immediately when they click a link.

Interesting things brewing in the Ember.js space.