Raging Slideshow 1.1.0

Appearantly Gnome does not implement the idle protocol. I therefore had to update my Slideshow for Gnome. It ran perfectly on KDE with Wayland using SwayIdle, but for Gnome on Wayland I had to come up with something else. Luckily the command

dbus-send --print-reply --dest=org.gnome.Mutter.IdleMonitor /org/gnome/Mutter/IdleMonitor/Core org.gnome.Mutter.IdleMonitor.GetIdletime

gives the idle time on Gnome. With this function in place my slideshow works on KDE and on Gnome with Wayland.
https://gitlab.com/raginggoblin/slideshow

Raging Slideshow

For years, I had a slideshow running in my living room, displaying all my digital photos in random order. Over time, I experimented with various screensaver applications, with xscreensaver’s glslideshow being the last one I used. However, I eventually grew frustrated with its inability to display the file path in a meaningful way. I didn’t want the full path cluttering the screen—just the subfolders, which would provide the context I needed to recall when and where a photo was taken.

Since my photos are meticulously organized in a structured folder system, I decided to solve the issue myself. I built a small JavaFX application, which has been running in our living room for the past few years.

Recently, I upgraded my home server and switched to Wayland, prompting me to update the application accordingly. At the same time, I figured—why not make it publicly available? So that’s exactly what I’m doing. The application is meant to run on Gnome with Wayland

Installation is still a pretty rough experience but I plan to improve this. Head over and check it out at: https://gitlab.com/raginggoblin/slideshow

Use Java as scripting language with a shebang and syntax highlightning

Recently I have been playing with Java as a scripting language and I must say, this could become a very nice way of scripting for me. Of course, there are always soreheads that will point out Java is too slow for this and that language X or Y is so much better suited for this task, but as I am writing Java on a daily basis, I am much quicker in writing a Java script (not a javascript!!!) than e.g. a bash script. And if you do not know Java, please keep your mouth, because you have no idea how powerful it actually is. As for startup speed of the jvm, this will become less an argument because of modularity and Graal.

Using the shebang in a Java file however will rob you of all the nice features an IDE is providing you as it has no way of knowing what the file actually represents. I therefore propose (and that is what I practice from now on) to use the file extension .jv. You can than tell your IDE that a jv file should be treated as a Java file.

shebangandcodecompletion
A .jv file treated as a java file

Move to Gitlab with a simple Java and Maven project

I recently decided to move all my code from Github to Gitlab. Moving the repositories was a breeze due to the import function of Gitlab. There is one single feature however that I am missing from Github. Gitlab does not have a proper release page. It is on the backlog (41766) of Gitlab, but I am afraid that it might take a little while before it lands into Gitlab. Gitlab provides a different mechanism to release your code using a build pipeline based on Docker and using git tags.

For a very simple Java (with Maven) project however, I found this a bit intimidating and therefore I decided to write a short introduction to get you going. This is the simplest of the simplest beginnings and only serves as a starter. But from hereon you can enhance your own pipeline and there are plenty resources on the web to get you moving on. Only this first start was somehow lacking in my opinion.

As said, the build pipeline of Gitlab is based on Docker and to get you going you will need a runner. Luckily when you are on gitlab.com this is already taken care of by a couple of shared runners, so no need to worry about this. To kickoff your pipeline add a file named

.gitlab-ci.yml

to the root of your project. The simplest form of this yaml file can be:

image: maven:3-jdk-8-alpine

maven_build:
   script: "mvn package -B"
   artifacts:
      paths:
      - target/your build jar.jar

 When you push this to Gitlab it will automatically kickoff a build for you at every commit.

A couple of things to note here. First, there is a reference to a Maven image. As the pipeline will use Docker, it will start a fresh container where Maven will not be available. So you need to tell the system to use an image that has Maven installed. You can find images at Docker hub. I just picked one with a Java 8 and a Maven 3 installation. The second thing to note is the build job I configured. The name (maven_build) does not matter, just pick one to your liking. The build command should be familiar to any Java developer, I just added the B flag so it will run without interruptions. At the end you will have to configure your artifacts. Otherwise, you will end up with artifacts only containing the sourcecode. You reference the jar which is normally available in the target directory. You can build your Maven project locally first to find out what your artifact will be named.

When you push this file you will find download buttons under CI/CD -” Jobs where you will be able to download your artifacts.

To further suit my needs I use a bit of an enhanced version of this configuration. E.g. I only build when I push a tag, otherwise I have to remove all the intermediate builds to prevent flooding the system with useless binaries. You can find an example here: MiWakeUpLight/.gitlab-ci.yml. Note that it is not possible to filter for a branch AND for a tag, the filtering is an OR filter (though a feature request exists).

For the rest everything is very well documented, but as a simple Java developer I had to adjust my workflow a bit to get me going on Gitlab.

MiWakeUpLight with WildFly Swarm

Recently I wanted to find out if WildFly Swarm is a real challenger for Spring Boot. Rather than creating another ‘Hello World’ application I decided to convert a project I use on an ‘almost’ daily basis to a WildFly Swarm application. This way I could compare the two frameworks. I created a clone of https://github.com/raginggoblin/MiWakeUpLight with WildFly Swarm instead of Spring Boot. You can find this project on https://github.com/raginggoblin/MiWakeUpLightSwarm. So there you can find a WildFly Swarm project that contains all technologies you want in a full stack application, i.e. a database (H2), JPA, Hibernate, datasources, CDI, logging, configuration with yaml, EJB, JaxRS and a AngularJs frontend on top.

CSV2CalDav

Sometimes a spreadsheet program is ideal for creating a schedule. But I could not find a way to import this into my calendar. In order to do this I wrote a small utility to convert a CSV file to a CalDav file. This CalDav file can be imported by any kind of calendering software.
For more information see: https://digitalcarpentry.nl/wordpress/csv2caldav
For downloads see: https://gitlab.com/raginggoblin/csv2caldav

Search Java SE API

For many years Sun and Oracle have failed to add a searchfield to the Javadocs found on http://docs.oracle.com/javase/7/docs/api/. Most of the time I use the Zeal API browser to search the Javadocs. But sometimes it is easier to let Google do the searching for you. To add a ‘site-specific’ search engine to Firefox add the following xml to a file in your profile directory inside the directory searchengines or searchplugins:

<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
                       xmlns:moz="http://www.mozilla.org/2006/browser/search/">
  <ShortName>Java SE7 API</ShortName>
  <Description>Search Java Standard Edtition API with Google</Description>
  <InputEncoding>UTF-8</InputEncoding>
  <Image width="16" height="16" type="image/x-icon">data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAHBPoACxfnAEtN7wCHhv4AAAL9AENF8QD29f8AwML/AAQA8wCHhf8AAAH+ABkT6ADv7v8AJiDpAAYE+ADx7v8A0tb/AAEJ5gDv8f8AAAT3APnu/wAAAvsAT1PnAAYD+QAIBPcADgf4AAoE9wACBP0AAQjnAPb0/wAAA/gAAgX0AAoM5gAAAvoABQP/AAAA/gACAvoAAgD+ANHV/wADAP4A6PD/AIiH/wDs8P8AAAP3ACUf6AADAfsARkj0AGBk8gCcnv8AAAH6AAcF+QAAAPwABwbwAOfv/wBOUuYACQbwAFdU8ACGhv8AAAP8ABER5wCIhv8AAAL+AHx59wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALSclJSETEx4THhMkJSclLSMVEwQKCgoKCgoKCgQTFSMECgoVHzQ3Nzc3NB8VCgoEKzMZDRY5KQkDPDkWDRkzKyI7LyYPKig1NSgqDyYvOyIIOBIMMAIuBQUuAjAMEjgIIAcGPgsaDgAyFxgLPgYHIBEUHQEEGzozMT0bBAEdFBwRFB0BBBs6MzE9GwQBHRQcIAcGPgsaDgAyFxgLPgYHIAg4EgwwAi4FBS4CMAwSOAgiOy8QDyooNTUoKg8QLzsiKzMZLDY5KQkDPDk2LBkzKwQKChUfNDc3Nzc0HxUKCgQjFRMECgoKCgoKCgoEExUjLSclJSETEx4THhMkJSclLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= </Image>
  <Url type="text/html" method="GET" template="http://www.google.com/search">
    <Param name="q" value="{searchTerms}"/>
    <Param name="sitesearch" value="http://docs.oracle.com/javase/7/docs/api/"/>
  </Url>
  <moz:SearchForm>http://www.world-connect-commodities.ro:8775/Search/default.aspx</moz:SearchForm>
</OpenSearchDescription>
Site specific search


For your convenience, you can download the file from here as well. After restarting Firefox you get a nice Javadoc search engine:

Infolog version 4.0 released

Infolog version 4.0 is released! The communication between the client and the server now uses a Rest webservice. This enabled me to write an Android client as wel. When upgrading, please note that data is now stored in json format. Before upgrading, make sure all your notes are on the server. See Infolog page for details about the program.

Spring Roo 10: Update images

In my previous blog about uploading images I left it to the reader to come up with a solution for updating an image once a LogItem is created. However, I recently noticed that I was not able to update this image as easy as I thought, due to the fact that the HiddenHttpMethodFilter provided by Spring is not capable of handling multipart forms. This will have the effect that when you click on an update button, you will end up creating a new one as the POST request will end up in this function. This is easy to remedy, but as it took me a while, I will put you in the right direction. We will continue where we left off the last time.

Step 9: Update update.jspx to send a MultipartFile

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:update id="fu_raging_goblin_roo_domain_LogItem" modelAttribute="logItem" multipart="true" path="/logitems/update" versionField="Version" z="user-managed">
        <field:input field="message" id="c_raging_goblin_roo_domain_LogItem_message" z="apnKidZldiFISmA4Q9pumki/o6Q="/>
        <field:datetime dateTimePattern="${logItem_creationdate_date_format}" field="creationDate" id="c_raging_goblin_roo_domain_LogItem_creationDate" z="p5GCO6w9q76CIDoEn17xbZPqlT0="/>
        <img height="256px" src="${logItem.id}/image"/>
        <field:input field="image" id="c_raging_goblin_roo_domain_LogItem_image" type="file" z="user-managed" required="false"/>
        <field:input field="contentType" id="c_raging_goblin_roo_domain_LogItem_contentType" z="user-managed" render="false"/>
        <field:input field="path" id="c_raging_goblin_roo_domain_LogItem_path" render="false" z="user-managed"/>
    </form:update>
</div>


Note several things. First, I changed the multipart to true, just like the create.jspx page. This will result in the error I described. Therefore I changed the path item so that it points to another path. Secondly, I set the input for the image to ‘not required’ as you might want to choose not to change the image. For the rest, changes are similar to create.jspx.

Step 10: Add an update method to the controller
The update method should be moved from the LogItemController_Roo_Controller aspect to the controller, which by now should be a very familiar trick. Change it to:

@RequestMapping(value = "/update", method = RequestMethod.POST, produces = "text/html")
public String update(@Valid LogItem logItem, BindingResult bindingResult,
        Model uiModel, HttpServletRequest httpServletRequest) {
    if (bindingResult.hasErrors()) {
        populateEditForm(uiModel, logItem);
        return "logitems/update";
    }
    uiModel.asMap().clear();

    CommonsMultipartFile image = logItem.getImage();
    if (image != null && image.getSize() != 0) {
        File file = new File(image.getOriginalFilename());
        try {
            image.transferTo(file);
            logItem.setContentType(image.getContentType());
            logItem.setPath(file.getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
            return "logitems/update";
        }
    } else {
        LogItem logItemFromDb = LogItem.findLogItem(logItem.getId());
        logItem.setContentType(logItemFromDb.getContentType());
        logItem.setPath(logItemFromDb.getPath());
    }

    logItem.merge();
    return "redirect:/logitems/"
            + encodeUrlPathSegment(logItem.getId().toString(),
                    httpServletRequest);
}


As you see, I put the method on the path /update, which corresponds with the value in update.jspx. The rest is pretty standard in my humble opinion.

Spring Roo 9: Uploading images 2 (store images on filesystem)

From the previous blog it is pretty easy to guess how to do this, but here is the recipe as promised to upload images and store them on the file system. As the images are not stored in the database you can use MySQL for this as well.

Step 1: Add maven dependencies
Same as previous.

Step 2: Add properties to the ‘LogItem’

private String contentType;
private String path;
@Transient
private CommonsMultipartFile image;

 Note here that the image is added as a CommonsMultipartFile, exactly as it is received by Spring, but it is marked @Transient which will prevent it from entering the database. Besides this a path is added to enable us to retrieve the image later.

Step 3: Update create.jspx to send a MultipartFile
Like in step 3 of the previous post, the form to create a LogItem is adjusted to our needs:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:spring="http://www.springframework.org/tags" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:create id="fc_raging_goblin_roo_domain_LogItem" modelAttribute="logItem" multipart="true" path="/logitems" render="${empty dependencies}" z="user-managed">
        <field:input field="message" id="c_raging_goblin_roo_domain_LogItem_message" z="apnKidZldiFISmA4Q9pumki/o6Q="/>
        <field:datetime dateTimePattern="${logItem_creationdate_date_format}" field="creationDate" id="c_raging_goblin_roo_domain_LogItem_creationDate" z="p5GCO6w9q76CIDoEn17xbZPqlT0="/>
        <field:input field="image" id="c_raging_goblin_roo_domain_LogItem_image" type="file" z="user-managed"/>
        <field:input field="contentType" id="c_raging_goblin_roo_domain_LogItem_contentType" render="false" z="user-managed"/>
        <field:input field="path" id="c_raging_goblin_roo_domain_LogItem_path" z="P0zJQZkpJ19wRptpdaKLWSTr/BU=" render="false"/>
    </form:create>
    <form:dependency dependencies="${dependencies}" id="d_raging_goblin_roo_domain_LogItem" render="${not empty dependencies}" z="Qbb30wyy/eoxYyR+b/J66Ec1Lxw="/>
</div>

Edit 17-07-2013: See next post how to handle updates to an image.

Step 4: Update input.tagx to handle type=”file”
If you have not done so yet, adjust it as described in step 4 earlier.

Step 5: Add a method to the controller to send the actual image
Images are now loaded from the filesystem, to be precise from the path field:

@RequestMapping(value = "/{id}/image", method = RequestMethod.GET)
public String showImage(@PathVariable("id") Long id,
        HttpServletResponse response, Model model) {
    LogItem logItem = LogItem.findLogItem(id);
    if (logItem != null && logItem.getPath() != null) {
        try {
            OutputStream out = response.getOutputStream();
            response.setContentType(logItem.getContentType());
            Files.copy(FileSystems.getDefault().getPath(logItem.getPath()),
                    out);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}

Step 6: Change create and update methods of LogItemController to save image to the filesystem
The create and update methods now should store the image on the filesystem. With the new file-io of Java 7 this has never been easier.

@RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(@Valid LogItem logItem, BindingResult bindingResult,
        Model uiModel, HttpServletRequest httpServletRequest) {
    if (bindingResult.hasErrors()) {
        populateEditForm(uiModel, logItem);
        return "logitems/create";
    }
    uiModel.asMap().clear();
 
    CommonsMultipartFile image = logItem.getImage();
    if (image != null) {
        File file = new File(image.getOriginalFilename());
        try {
            image.transferTo(file);
            logItem.setContentType(image.getContentType());
            logItem.setPath(file.getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
            return "logitems/create";
        }
    }
    logItem.persist();
    return "redirect:/logitems/"
            + encodeUrlPathSegment(logItem.getId().toString(),
                    httpServletRequest);
}

Note that the images will end up in the same directory where your webapp is running. This is something you probably wouldn’t do on a production system.

Step 7: Give webserver write access to the filesystem
Take care that the directory where you want to save the images is write-enabled for the webserver. Ok, I admit this is not really a decent step, but I just wanted to keep the numbering the same as in the previous post and this is the best I could come up with ;).

Step 8: Update show.jspx to display the image
The artificial step 7 allows me now to just redirect you to the previous step 8.