<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[GSMBlog]]></title><description><![CDATA[Questions of the What-is-x? form is, perhaps, when unsupported by context, the vaguest of all forms of questions except an inarticulate grunt.]]></description><link>https://gsmblog.net/</link><image><url>https://gsmblog.net/favicon.png</url><title>GSMBlog</title><link>https://gsmblog.net/</link></image><generator>Ghost 3.42</generator><lastBuildDate>Fri, 29 Aug 2025 08:13:07 GMT</lastBuildDate><atom:link href="https://gsmblog.net/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Falling in love with a font]]></title><description><![CDATA[After using the same font for everything, I recently fell in love with this absolutely beautiful and extremely readable font.]]></description><link>https://gsmblog.net/falling-in-love-with-a-font/</link><guid isPermaLink="false">61524db73e5ceb0001f9f050</guid><category><![CDATA[font]]></category><category><![CDATA[emacs]]></category><category><![CDATA[terminal]]></category><category><![CDATA[ascii-art]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Mon, 27 Sep 2021 23:29:58 GMT</pubDate><media:content url="https://images.unsplash.com/reserve/uZYSV4nuQeyq64azfVIn_15130980706_64134efc6e_o.jpg?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDl8fGZvbnR8ZW58MHx8fHwxNjMyNzg1MTU3&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/reserve/uZYSV4nuQeyq64azfVIn_15130980706_64134efc6e_o.jpg?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDl8fGZvbnR8ZW58MHx8fHwxNjMyNzg1MTU3&ixlib=rb-1.2.1&q=80&w=2000" alt="Falling in love with a font"><p>After using the same font for everything, I recently fell in love with this absolutely beautiful and extremely readable font.</p><p>For years and years I've been using a terminal <a href="https://gsmblog.net/tag/font">font</a> that I modified to add support for <a href="https://gsmblog.net/tag/ascii-art">ASCII-art</a> and some Menlo Powerline glyphs. I was quite satisfied with the font, but over the last few years I've noticed that my eyesight isn't what it once was, and I've had more trouble reading the font and having to zoom things in more often. Also, the ASCII-art got screwed up at a few font sizes. Finally I simply have had a feeling lately that the font didn't "pop" as much as it once did. (I cannot for the life of me remember what font I based it on, and I guess it really doesn't matter).</p><p>The other day while watching an <a href="https://gsmblog.net/tag/emacs">Emacs</a> tutorial on YouTube, my jaw dropped. The font used by the author of the video looked, in my honest opinion, stunning. And, at least for me, was extremely readable compared to anything I've used before, even when very small. </p><p>I trawled github to find out what font it was. And finally, I found it: <a href="https://typeof.net/Iosevka/">Iosevka</a>. (And just in case the web site disappears sometime in the future, the font is available on <a href="https://github.com/be5invis/Iosevka/releases">github</a>.</p><!--kg-card-begin: html--><div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div><!--kg-card-end: html--><p>Not only is this font good looking and readable, but it's also very impressive. The font is actively developed. It has an insane amount of glyphs available, and more are being added. It comes in many different varieties, and each variety has a full range of variants. Everything is available as TTC, TTF and webfonts. It even has an online <a href="https://typeof.net/Iosevka/customizer">customization tool</a> that allows you to generate fonts with only the exact variants, glyphs and varieties you need. To see every glyph available in every variety and variant, there is a <a href="https://typeof.net/Iosevka/specimen">specimen tool.</a></p><p>This is now my default font for more or less everything I do, and I highly recommend checking it out, and testing it your self.</p>]]></content:encoded></item><item><title><![CDATA[Bridge your smart home with Google Home (or Alexa)]]></title><description><![CDATA[This fancy little plug in and forget gives you proper integrations between your smart home and your Google Assistant (or Alexa).]]></description><link>https://gsmblog.net/bridge-your-smarthome-with-google-home-or-alexa/</link><guid isPermaLink="false">603807d05fca66000133c1a0</guid><category><![CDATA[home automation]]></category><category><![CDATA[google]]></category><category><![CDATA[alexa]]></category><category><![CDATA[fibaro]]></category><category><![CDATA[futurehome]]></category><category><![CDATA[android]]></category><category><![CDATA[raspberry pi]]></category><category><![CDATA[smart home]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Thu, 25 Feb 2021 21:11:55 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2021/02/ab_actions_flow-1024x683.png" medium="image"/><content:encoded><![CDATA[<img src="https://gsmblog.net/content/images/2021/02/ab_actions_flow-1024x683.png" alt="Bridge your smart home with Google Home (or Alexa)"><p>A long time ago, and as soon as it was commercially available, I purchased an <a href="https://automationbridge.com.au">automationbridge</a>. I was so stoked about this product, and it's been a central but silent part om my smart home ever since. </p><p>The reason I was so stoked was because at the time my main smart home controller was a Fibaro Home Center 2. Which is a great device, and I highly recommend it if you want a perfect balance of things that just work, but still be able to do things your self (using LUA, which off course isn't optimal, but hey it's better than nothing). But let's face it. Fibaros Google Home integration sucks big time. </p><p>Which leads me to the automationbridge. This fancy little device (it's a raspberry pi with custom software on an SD-card glued stuck), bridges your smart home controller with Google Home or Alexa, and lets you easily do anything you can do in your smart home through voice commands (and much much more). It gives you Google Home and Alexa integration the way things should have been done.</p><!--kg-card-begin: markdown--><div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div><!--kg-card-end: markdown--><p>Currently the supported smart home controllers are Fibaro Home Center HC2 &amp; HC3, Fibaro Home Center Lite, Vera Edge/Plus/Secure, HomeSeer, Home Assistant, RTI XP Controllers. </p><p>But it can do a lot more. It also has modules for a bunch of third party integrations such as Logitech Harmony Hub, Netamo, Belkin Wemo, Bose SoundTouch, Sonos, LIFX, etc. The list is way to long to show everything here. Just be aware that these additional plugins cost extra, and you purchase them individually. I haven't used any of these, but one of these days I'm going to pull out my Harmony Hub and connect it, and find something smart to use it for. </p><p>The automation bridge is super easy to use. You simply plug it in, connect it to your network, and run through a very easy to follow setup. This is a true plug and forget device. The only problem with that is I log on to it about once a year, realize it has a bunch of firmware updates to be installed. I install them, and realize that they have added a bunch of new features that I probably should investigate, but never get around to.</p><p>A different, really great feature is the notification feature. This allows you to send audio notifications to one, or all of your google home devices directly from a script on your home automation controller. You can also set up default notifications that trigger based on events from your controller, with a custom message, and custom set of Google Home device targets, directly on your automation bridge. Only your imagination limits this. I've among other things used it to have a voice inform the burglar that the alarm has been triggered, and warn me if the balcony temperature is too high and my plants are going to be unhappy.</p><p>But notifications allow you to do more. Based on events you can also trigger sending a message to Pushover, or call any number of custom webhooks. I haven't used these additional notification features, as they were a later addition, and I already have my own custom Pushover and webhook integration I wrote myself running on the Fibaro Home Center 2.</p><p>Finally it also has a feature for building panels for use on wall mounted Android tablets or iPads. I haven't tested this feature as I prefer to make my own wall panels running on raspberry pi with my own custom software. </p><p>So yeah. If you use Google Home, and have one of the listed smart home controllers I highly recommend investing in one of these devices, as they make life easier and makes your smart home just that much more smarter. </p><p>By the way, the Fibaro Home Center 2 is still my main smart home controller, but it now runs in tandem with a Futurehome Hub.</p>]]></content:encoded></item><item><title><![CDATA[Google Assistant on the desktop]]></title><description><![CDATA[Google Assistant on the desktop? It's finally possible. I've been waiting for this for ages.]]></description><link>https://gsmblog.net/google-assistant-on-the-desktop/</link><guid isPermaLink="false">6037df9e5fca66000133c109</guid><category><![CDATA[android]]></category><category><![CDATA[google]]></category><category><![CDATA[software]]></category><category><![CDATA[home automation]]></category><category><![CDATA[smart home]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Thu, 25 Feb 2021 20:20:03 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2021/02/google_assistant_unofficial.png" medium="image"/><content:encoded><![CDATA[<img src="https://gsmblog.net/content/images/2021/02/google_assistant_unofficial.png" alt="Google Assistant on the desktop"><p>So ever since google tricked me into heavily going into their home automation ecosystem, by asking if I wanted to help them test something cool which I would have to sign an NDA for, which turned out to be a google home mini that supported Norwegian before they launched in Norway, I've used google assistant a lot. (So one of my google home minis has a sticker on it saying it is a preview and is illegal to sell, trade or give away).</p><p>Google home is everywhere on in my life. I've got lots of google home, google home minis and google home hubs all throughout the apartment. I use google assistant on my phone. I've integrated <a href="https://gsmblog.net/android-app-tasker/">tasker</a> heavily with google home, and plan on setting up multiple old phones with tasker for various other integrations. I use a automationbridge (I'm writing a post about this product soon) to integrate my Fibaro Home Center 2. I've got lots of custom stuff running on raspberry pi's. And lot's of other google home integrations going on.</p><p>Oh, and did I mention that "ok Google, remind me to ...." basically organizes my whole life?</p><p>Anyhow. I've always felt that one thing was missing from the ecosystem. A good desktop client. When working from my laptop, or my stationary computer, talking to a google appliance across the room, or grabbing my phone is just a pain. It takes extra time, and it disrupts my work flow. I've always wanted to be able to with a keyboard shortcut send a few lines of text to google assistant. But this hasn't been possible, until now. (It's also possible to use your voice, but keyboard is so much faster)</p><p>I happened to stumble across this project randomly, and it's everything I was looking for. The <a href="https://github.com/Melvin-Abraham/Google-Assistant-Unofficial-Desktop-Client">Google Assistant Unofficial Desktop client</a>. And even better, it is cross platform, so it works on Windows (yuck), it works on Mac (a little less yuck), and it works on GNU/Linux (yay). </p><figure class="kg-card kg-image-card"><img src="https://gsmblog.net/content/images/2021/02/Assistant_light_dark.jpg" class="kg-image" alt="Google Assistant on the desktop" srcset="https://gsmblog.net/content/images/size/w600/2021/02/Assistant_light_dark.jpg 600w, https://gsmblog.net/content/images/size/w1000/2021/02/Assistant_light_dark.jpg 1000w, https://gsmblog.net/content/images/2021/02/Assistant_light_dark.jpg 1529w" sizes="(min-width: 720px) 720px"></figure><p>There isn't that much more to say about the functionality. It is what it says it is. Google assistant on the desktop. However, there are a few things you should be aware of. First of all don't expect all of the features you find on your phone, or on your google home device for that matter. The app is unofficial, but uses Googles official public API. That means that you only have access to the features Google has chosen to provide through their API. Some features they reserve for their own internal implementations, and such features naturally aren't available.</p><!--kg-card-begin: html--><div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div><!--kg-card-end: html--><p>In addition the project is young. Hence there are bugs, and the feature list isn't huge. But it is under continuous and active development. So more features are continuously added.  </p><p>Finally a note on installation is needed. Since this is unofficial, the activation process needed isn't trivial. You need to register to get access to the API, and register the app as a trial application. That means this isn't for the people who simply can't handle anything more advanced that plugging something in to the wall (of people who feel they are to fancy for anything other than plugging something in to the wall and will gladly pay through their nose and use sub par products and services to prove their point - I'm looking at you Apple users). Fortunately, the main developer has made a very clear and concise <a href="https://github.com/Melvin-Abraham/Google-Assistant-Unofficial-Desktop-Client">illustrated guide</a> to installing and activating the app, so anybody who can read and see pictures should be able to install it. </p><p>So if you use Google Assistant, I highly recommend you <a href="https://github.com/Melvin-Abraham/Google-Assistant-Unofficial-Desktop-Client">try out this app</a>.</p>]]></content:encoded></item><item><title><![CDATA[Let's encrypt everything!]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://gsmblog.net/tag/https">https</a> is easy, simple, free, and soon to be &quot;mandatory&quot;. All of my sites and services use https, always. This was done as a project over time quite a few years ago, combined with converting all of my sites and services to run on <a href="https://gsmblog.net/tag/docker">docker</a> which I touched</p>]]></description><link>https://gsmblog.net/lets-encrypt-everything/</link><guid isPermaLink="false">6037ddd40cae840001e946c8</guid><category><![CDATA[https]]></category><category><![CDATA[docker]]></category><category><![CDATA[encryption]]></category><category><![CDATA[wifi]]></category><category><![CDATA[html]]></category><category><![CDATA[api]]></category><category><![CDATA[lisp]]></category><category><![CDATA[kubernetes]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Thu, 01 Mar 2018 15:44:07 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/03/ssl.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/03/ssl.jpg" alt="Let's encrypt everything!"><p><a href="https://gsmblog.net/tag/https">https</a> is easy, simple, free, and soon to be &quot;mandatory&quot;. All of my sites and services use https, always. This was done as a project over time quite a few years ago, combined with converting all of my sites and services to run on <a href="https://gsmblog.net/tag/docker">docker</a> which I touched on in my previous post, and will be covering in the future.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<p>Originally, everything ran on a couple of payed, catch-all wildcard certificates from startssl. After <a href="https://letsencrypt.org/">let's encrypt</a> launched, I decided to move everything over to them. Not really to save money, but rather because I had a feeling that let's encrypt would be huge, and something that I really should learn more about as soon as possible. And boy was I glad I did when the whole startcom drama occurred.</p>
<p>In this post I will describe the technology I used to create a zero maintenance let's encrypt based system for all of my sites and services, including automatic http-&gt;http redirects, that is future proof (meaning that adding new services or sites require zero effort regarding https, as long as they run in docker). But first a few thoughts on https in general.</p>
<h1 id="whyhttpsisimportantandsomethingyouneedtoknow">Why https is important, and something you need to know</h1>
<p>Back in the days, https was something you needed if you were an online bank, doing e-commerce, running a secure corporate network, or something else &quot;important&quot;. Some years ago, this started to change. It started as a trickle, but recently the faucet has been opened, and now everybody online needs to relate to it. If you publish stuff online or run any kind of online service, there is no way around it. If you are &quot;only a consumer&quot; you still need to relate to it, and if you don't you will be in trouble at some point. Hint: Do you always ensure any site you enter any personal information on is encrypted?</p>
<p>The main deal here is that you need to ensure that all of your personal information is secure online, including your browsing habits. These days, anything you do online without https (or when talking about non-web stuff, without <a href="https://gsmblog.net/tag/encryption">encryption</a>) is up for grabs by anybody between you and whatever end point you are accessing. This isn't anything new, but awareness of it is much greater, and it's no longer only the &quot;nerds in the closets&quot; who have access. It could be anybody. And if you are on a non-private wireless network (which is more and more common, <a href="https://gsmblog.net/tag/wifi">wifi</a> is everywhere), you can almost guarantee that the baddies have all of your unencrypted information.</p>
<p>In other words, doing anything unencrypted online today is a bad idea, period. But wait, you might say, I don't have anything to hide. Hint: Everybody has something to hide, you might just not think about it all the time. Would you post your VISA number and PIN-code online? Would you post your social security number online? Would you post all of your web history online? Even if you somehow exist in a parallel reality where you would post all of these things online, by not using encryption you are harming those whose lives depend on encryption being available. There are lots of people out there whose lives depend on being able to use encryption freely, such as dissidents in suppressive states and dictatorships, whistle blowers, people fleeing from persecution, abusive relationships or even the mafia. If you use encryption when you strictly don't feel the need to, you help create plausible deniability for those who do need it, and you help ensure encryption options exist. If you don't you indirectly harm them. Off course, this is an even broader area than just https. Encrypted email, chat, voice, etc. is just as important.</p>
<p>By always using encryption you are also protecting your self from spoofs, traps and tricks. There are lots of ways to attempt to fool you into believing you are visiting a different site than you actually are. The reasons can be many, including stealing your information, tricking you into clicking on ads to generate revenue for somebody, or installing viruses or malware on your computer. By always using https, some of, but of course not all of these tricks are foiled.</p>
<p>Finally, because of all of the issues mentioned above, browser vendors are starting to enforce the use of https. This includes showing a big red warning to all users that your site is not safe if you don't use https, blocking any new web functionality, so no https, no new web features will work on your site, and down right blocking you completely. The vendors started slowly, but are getting more and more aggressive, and we are most likely not far from a situation where https basically is mandatory for all presence on the web. The unencrypted web is soon to be broken. If you haven't already, now is the time to prepare.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h2 id="prosandcons">Pros and cons?</h2>
<p>There are two options. Embrace the change and be happy, or complain and make a fuss. But in the end, complaining will get you nowhere. This is happening, it's happening now, and it's happening fast. It (should) impact everybody, and nobody can change it. So embrace it instead. Hence I'm not going to say much about pros and cons. But one of each are worth mentioning.</p>
<p>The main pro is of course that this is needed. And it has been needed for a long time. So that this is finally happening is great.</p>
<p>The main con has to do with access to technology. Web publishing of simple content is easy, very easy. All you need is a server, or a computer that always is on, and a public IP-address or a forwarding service. In addition you need to acquire some simple knowledge of <a href="https://gsmblog.net/tag/html">HTML</a> and how to install a web server. That's it. But once https is defacto required for everything, since your published content will be banned by the browsers, much more technical knowledge is needed, and it is no longer a trivial task to learn. This is a shame since it hinders access to knowledge, and can kill the joy of learning by making the initial threshold higher.</p>
<h1 id="letsencrypt">Let's encrypt</h1>
<p>To make the mentioned shift possible,the Mozilla foundation started working on a free, easy to use system called Let's encrypt. Today, a lot of major players sponsor the Let's encrypt. Https used to be expensive, and fairly unavailable. Let's encrypts goal was to change this.</p>
<p>Let's encrypt is free to use, and you can create as many certificates as you want for free. You can do it manually, but the certificates are only valid for three months at a time. Fortunately Let's encrypt provides a very good <a href="https://gsmblog.net/tag/api">API</a> that allows easy automation of certificate creation and renewal. Furthermore, basically the whole world of service providers and application developers have embraced Let's encrypt. Most applications or services either provide a plug-in TODO TAG PLUG-IN? or already have Let's encrypt built in. You'll also find multiple API-implementations for basically any programming language out there, even <a href="https://gsmblog.net/tag/lisp">lisp</a>.</p>
<p>Let's encrypt makes basic https free and available for everybody, which is great. Advanced use, the kind of use required by banks, e-commerce, etc. isn't covered, but that's ok. This service is for the broad public, not for the huge corporations.</p>
<h2 id="technicalawesomeness">Technical awesomeness</h2>
<p>I promised you some technical goodies, and here they are. As mentioned in my previous post, I have converted all of my sites and services to run on <a href="https://gsmblog.net/tag/docker">docker</a> over the last few years. I'm more or less in love with docker, and basically have my whole life in docker these days. As part of this process I converted everything to use https. Initially I used a master certificate I payed for, but as soon as Let's encrypt was mature, I decided to switch, a choice I'm very happy with. Fortunately, I decided to try to make a system that was automatic, and future proof. In other words, no maintenance needed, and adding new sites or services should be trivial.</p>
<p>While converting all of my sites and services to docker, I also set up https for all of them. This was done using a single nginx instance running in docker using the fabulous docker image <a href="https://github.com/jwilder/nginx-proxy">jwilder/nginx-proxy</a>. This runs as a single nginx container instance, and listens to docker waiting for new containers to be started. By adding a few environment variables when spinning up a new container, you can instruct nginx-proxy that you want to be proxied with the given url. Nginx-proxy automatically detects this, automatically creates a proxy configuration, and reloads itself. Hence you can automatically add new services to your proxy by simply adding a few environment variables to the command when starting the service. No need for any manual configurations or restarting containers. Nginx-proxy handles everything.</p>
<p>Nginx-proxy has a single configuration copied and used for all existing and new proxied containers. In addition, you can specify individual configurations for individual containers (based on hostname) if needed, but naturally this process does require a reboot. If you already have existing purchased https certificates, you can add them to nginx-proxy, again identified by host name. This is how I originally did things. Finally, the proxy will, when you use https, ensure that any unsecure requests automatically are re-directed to secure requests. This can be disabled if you want to, though I cannot fathom why you would. Not only is this important to ensure the security of your users, but google will index your secure and non-secure sites as separate sites, so serving content (the same or different) on both can cause issues with your search rankings.</p>
<p>I plan to give a much more detailed description of the setup I've used for docker in a later post.</p>
<h3 id="addingletsencrypttothemix">Adding Let's encrypt to the mix</h3>
<p>Since I already had this system when I wanted to convert to Let's encrypt, I decided to investigate if I could find something that worked well with the system I already had. And fortunately I did.</p>
<p>The component I found was <a href="https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion">JrCs/docker-letsencrypt-nginx-proxy-companion</a>. This nice little component runs as it's own container and hooks into the system described above. It allows using Let's encrypt as a part of the system, again by adding a few environment variables when creating a container, in the same way as with nginx-proxy. These environment variables instruct the companion to retrieve a certificate from let's encrypt if an existing valid certificate doesn't exist. The companion then handles the certificate generation, and instructs the proxy to use that certificate for the new container it has been instructed to proxy.</p>
<p><img src="https://gsmblog.net/content/images/2018/01/schema.png" alt="Let's encrypt everything!"></p>
<p>This makes adding certificates to the system easy as can be. All you need to do is add a couple more environment variables to the command you use to create your instance, and bobs your uncle.</p>
<p>The companion also ensures the certificates stay up to date—remember they are only valid for three months—without any manual interaction. In addition to the daemon listening for new containers, the companion runs a &quot;cron job&quot; that periodically checks the validity of the certificates in use. By default, any certificate that will expire in less than a month, will automatically be renewed.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h3 id="shortcomingsofthesetup">Shortcomings of the setup</h3>
<p>The only shortcoming I've found with the system, is an issue that most likely not many people will encounter. The shortcoming only applies to the Let's encrypt companion. Basically, the companion doesn't handle multiple instances very well. If you run multiple instances of it on the same system, they will all trigger on all actions, and try to create or renew certificates that they themselves don't &quot;own&quot;, resulting in error messages, and unneeded communications with the Let's encrypt API. The system is still fully usable, but the added error messages in the log, and added traffic to Let's encrypt are annoying.</p>
<p>And in case you wonder why I would have two instances, the reason is I have individual nginx proxies listening to two different IP-addresses on the same machine. I initially had to do this to allow incoming webhooks from a system that couldn't handle wildcard certificates. This prompted me to set up a different IP-address on the same system, not only for webhooks from the mentioned system, but all incoming webhooks. Feels nice to be able to keep incoming webhooks separate from the rest of the system.</p>
<h3 id="shouldidothesame">Should I do the same?</h3>
<p>You might be planning on doing something similar yourself, and wondering if you should create a setup similar to the one I created. The answer to this is: probably not.</p>
<p>I learned a great deal and had a lot of fun while implementing the system, and I'm very happy with the result. I now have a stable, reliable and extendable system that requires little to no maintenance. However, getting there was not trivial. It required a lot of trial and error, reading documentation, reading code when documentation wasn't available (more often than not), and a couple of restarts from scratch.</p>
<p>Unless you actually want to do all of this tinkering (for fun, or for profit) or you want to learn more about docker, let's encrypt and nginx, you probably shouldn't go down the same route I did.</p>
<p>If you simply want something robust that works, you probably are better of going with a more &quot;out of the box&quot; system, such as <a href="https://traefik.io">Træfik</a>. Granted, learning Træfik is probably just as much work as setting up the system I described above, but you'll get a whole lot more for free (such as a nice UI, clustering support, metrics, an API, etc.) You will also be learning something you much more likely will encounter in &quot;the real world&quot;. Documentation is also a lot better, and I'm guessing that the technology most likely will exist longer into the future as well.</p>
<p>At some point I want to convert my system to use Træfik, and potentially also <a href="https://gsmblog.net/tag/kubernetes">Kubernetes</a>, but that is a task and a blog post for another day, and not something I highly prioritize at the moment.</p>
<p>If you only take one thing away from this blog post, I hope it's that you have to be conscious of https and encryption and actively relate to it, no matter who you are.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[gsmblog.net revamped]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>You might have noticed recently, that gsmblog suddenly got a new fresh modern design. But this isn't a simple re-design. This is a complete revamp, and a total re-write from scratch. And it didn't happen over night. The future of the blog is bright.</p>
<p>Before I continue, I want to</p>]]></description><link>https://gsmblog.net/gsmblog-net-rewamped/</link><guid isPermaLink="false">6037ddd40cae840001e946c7</guid><category><![CDATA[misc]]></category><category><![CDATA[varnish]]></category><category><![CDATA[php]]></category><category><![CDATA[software]]></category><category><![CDATA[html]]></category><category><![CDATA[css]]></category><category><![CDATA[javascript]]></category><category><![CDATA[api]]></category><category><![CDATA[markdown]]></category><category><![CDATA[wysiwyg]]></category><category><![CDATA[docker]]></category><category><![CDATA[linode]]></category><category><![CDATA[debian]]></category><category><![CDATA[guice]]></category><category><![CDATA[gui]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Mon, 15 Jan 2018 20:19:14 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/photo-1488190211105-8b0e65b80b4e.jpeg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/photo-1488190211105-8b0e65b80b4e.jpeg" alt="gsmblog.net revamped"><p>You might have noticed recently, that gsmblog suddenly got a new fresh modern design. But this isn't a simple re-design. This is a complete revamp, and a total re-write from scratch. And it didn't happen over night. The future of the blog is bright.</p>
<p>Before I continue, I want to make one, rather bold, statement. The blog has basically been dead for a long time now. That <strong>was</strong> due to a couple of things. First of all, life happened. I've been busy with a new job, a new apartment, lots of personal projects, and generally lots of stuff going on. Secondly, even though the blog was in its fourth third iteration, I was far from happy with the tech. It started on Joomla, moved on to Drupal, and finally Wordpress. But none of them were good. They were large, heavy, slow (hence needed <a href="https://gsmblog.net/tag/varnish">varnish</a> to function properly), and based on <a href="https://gsmblog.net/tag/php">php</a> (so based on a nasty language with fairly sloppy code, and full of security issues). Now, in its fourth iteration, I think I finally have found the ultimate permanent solution. More on this soon.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<p>Currently the whole blog has been converted. The only thing missing is comments. I've moved to using an external comment system to make life easier, and it seems that importing the old comments will require some manual work, but I will move the few relevant comments over at some point.</p>
<h1 id="introducingghost">Introducing Ghost</h1>
<p>Without further ado, let me present <a href="https://ghost.org/">Ghost</a>. I came across Ghost when looking for a simple blog <a href="https://gsmblog.net/tag/software">software</a> for work. We needed something simple. Something that a bunch of nerdy tech heads could use without annoyance, without lots of bells an whistles. And, since we also wanted to use it as a first steppingstone to start moving services to <a href="https://gsmblog.net/tag/cloud">the cloud</a>, we wanted it to be docker friendly. After testing and evaluating a bunch of different alternatives, I came across Ghost. And I was really impressed, even if it wasn't even a full release version yet. So, why so great?</p>
<h2 id="moderntechnologyapi">Modern technology. API</h2>
<p>First off, Ghost is based on modern <a href="https://gsmblog.net/tag/javascript">Javascript</a> technology. Now, I'm not always a huge fan of modern Javascript &quot;hipster&quot; technology, but when done right, I like it. And Ghost did it right. Not only is it modern, but their choice of technology makes it lightning fast out of the box. No need for varnish any more. And their markup (<a href="https://gsmblog.net/tag/html">HTML</a> and <a href="https://gsmblog.net/tag/css">CSS</a>) is modern as well.</p>
<p>Ghost is also fully <a href="https://gsmblog.net/tag/api">API</a> based, with both a public and a private API. So no vendor lock in, data is available, and simple to export or use within other applications. My personal opinion is that any data that isn't available through an API is useless, and nobody should publish such data. Also, all of the data is stored in a very simple, available format, easy to extract even without the API.</p>
<h2 id="kissmarkdownandantiwysiwyg">KISS, markdown and anti-WYSIWYG</h2>
<p>Possibly even more important is the wonderful editor, their KISS (Keep it stupid simple) philosophy and anti-<a href="https://gsmblog.net/tag/wysiwyg">WYSIWYG</a> stance. The editor is based on <a href="https://gsmblog.net/tag/markdown">markdown</a>, and as a nerdy guy who writes code, any software that allows me to use some sort of <a href="https://gsmblog.net/tag/markup">markup</a> language, like markdown is great. It's easy, flexible, simple and available, and doesn't require me to use that pesky mouse. Finally, they have done something, that I really wish more products did, and made the markup first citizen and downplayed WYSIWYG. I loathe WYSIWYG editors, and avoid them at all costs, and Ghost has provided a few buttons for those who absolutely have to have some sort of <a href="https://gsmblog.net/tag/gui">GUI</a>, and preview mode. In other words, a perfect balance. Finally full HTML can be inserted anywhere, which is great.</p>
<h2 id="fastandflexible">Fast and flexible</h2>
<p>Ghost is also very simple and easy to deploy and configure. A few simple commands are needed, and a couple of settings in a config file are needed, and you are set to go. Or, if you use <a href="https://gsmblog.net/tag/docker">docker</a> all you have to do is to pull and run the docker container, no configuration needed. Once you are done testing or creating your installation, preparing for production is even simpler. All you need to do is change a single parameter in the configuration file, or if you use docker, add an environment variable to your docker command.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<p>Creating a theme for your blog (if you don't want to purchase a theme in the marketplace, or stick with the default theme) is simple, understandable and, for a developer, trivial. The easiest way is to start with the default theme and adapt it to your needs. This couldn't be easier. You simply clone the git repository containing the default theme. Run <code>npm install</code> and away you go. Naturally you have automatic compilation on changes. Once you are done, you run a single build command, and upload the resulting zip to your blog. The css and templates are clean, easily understood, and the documentation is good.</p>
<p>I don't have any experience (yet) extending or adapting Ghost, but from the looks of it, this should also be fairly easy as long as you know a bit about modern Javascript development.</p>
<h2 id="arethereanydownsides">Are there any downsides?</h2>
<p>Not many. But note that if you are the kind of person who wants writing your blog to be similar to writing a document in Microsoft word, or you feel the need for tons of glorious bloating extensions, Ghost is not for you.</p>
<p>There are a couple of things I really would like though. I would appreciate a way to change and add keyboard shortcuts in the editor. The fact that I try to avoid the mouse as much as possible, and use a browser, and browser plug-ins geared to always using the keyboard, the editor comes up short when it comes to keyboard interaction.</p>
<p>Also, the editor doesn't play nice with browser plug-ins that allow you to use external editors to edit in-browser text, and has issues with in-browser spell-checking. Somehow the editor seems to not be identified as an input, which causes issues.</p>
<p>The tag-editor unfortunately lacks a couple of important features. The tags need to be ordered in alphabetical order, and allow ordering by frequency and order of creation. As of now, the only way to find tags is by searching, which is a pain.</p>
<p>Finally, a feature for custom snippets would be highly appreciated. Nothing fancy is needed, but a simple key-&gt;html addition would make life a lot easier. Optionally, one could allow the user to create markdown extensions.</p>
<h1 id="thefutureoftheblog">The future of the blog</h1>
<p>Hopefully this means that posts on this blog will be much more frequent in the future. There no longer is any kind of technical or WYSIWYG-annoyance hindering frequent posting. In addition, my new job provides me more spare time, and a much better possibility of investigating and learning new technology pro-actively on the job, rather than always just last minute learning based on whatever is needed for the next project. The list of posts I want to write has been steadily growing, so I'll most definitely never run out of topics to cover.</p>
<h1 id="asmalltechupdate">A small tech update</h1>
<p>About a year and a half ago, I spent some time upgrading all of my sites, and all of my services to run on docker and got rid of my host running on <a href="https://gsmblog.net/tag/tilaa">tilaa</a> and moved everything back to my original <a href="https://gsmblog.net/tag/linode">linode</a> server. I might use tilaa for some things yet in the future, if nothing else for prototyping, but when linode dropped their prices tilaa no longer offered any big benefits compared to linode, and after experiencing some random kernel panics on their custom <a href="https://gsmblog.net/tag/debian">debian</a> without them being able to determine why, I opted out for now.</p>
<p>Anyhow, the migration was a long ongoing process as I not only moved everything over to docker, but changed a bunch of technology stacks. One of the major changes was re-creating all of my static sites using <a href="https://gsmblog.net/tag/spring">spring</a> <a href="https://projects.spring.io/spring-boot/">boot</a>. But a lot of other technology was modernized and replaced as well. I had planned a long series of posts about the process, but as mentioned, life happened. Some of the planned posts should appear in the near future though.</p>
<p>As mentioned, everything now runs on docker (basically my whole life is in docker now) which is a huge time saver, and makes life much easier and much more fun. I cannot recommend docker enough.</p>
<h2 id="futureplans">Future plans</h2>
<p>Over time I'm hoping to continue to modernize my technology stack, and also complete some of the many side projects I've been planning for ages. At some point I would like to move things from bare bone hosting into <a href="https://gsmblog.net/tag/kubernetes">kubernetes</a>, however, at the moment google cloud (which I've been starting to investigate at work) is way to expensive compared to linode, and even self hosting kubernetes would be much more expensive than the bare bone hosting I currently do.</p>
<p><small>Photo by <a href="https://unsplash.com/@thoughtcatalog">Thought Catalog</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Always publish your libraries to maven repositories]]></title><description><![CDATA[You should always publish your open source libraries to public maven repositories. It's painless, free and simple and saves everybody a lot of time.]]></description><link>https://gsmblog.net/always-publish-your-libraries-to-maven-repositories/</link><guid isPermaLink="false">6037ddd40cae840001e94615</guid><category><![CDATA[programming]]></category><category><![CDATA[docker]]></category><category><![CDATA[git]]></category><category><![CDATA[github]]></category><category><![CDATA[gradle]]></category><category><![CDATA[maven]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Fri, 01 May 2015 18:58:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2017/06/code-cover.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2017/06/code-cover.jpg" alt="Always publish your libraries to maven repositories"><p><a href="https://gsmblog.net/tag/maven">Maven</a> is a great tool. I could spend a lot of time and effort telling you why, but if you use maven or have moved on to <a href="https://gsmblog.net/tag/gradle">gradle</a> or something similar, you already know why. That is not the point of this post. The point of this post is to get you, the open source software developer, to embrace maven the way it should be embraced.</p>
<p>One of the really great things about maven is that it, combined with a good version control system, such as <a href="https://gsmblog.net/tag/git">git</a>, allows you to work on your projects most anywhere with little to no effort. This gets even better when you combine it with something like docker, which allows you to create developer environments anywhere with little to no effort. Have a few hours to kill at a friends house or the library? Check out your project and work on it. Need to stay home from work to take care of your sick kids? Check out your project and work on it. Attending a dull meeting or conference? Check out your project and work on it.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<p>Now in theory this works great. Unfortunately it's only mostly in theory. We live in a great time to be an open source dev. 99% of the time you can find libraries for anything you need thanks to <a href="https://gsmblog.net/tag/github">github</a> and friends. Unfortunately most of those libraries aren't available in a maven repository. You have to clone the project, and install it locally. Which sucks. It makes things hard. And it makes the scenario I described above a pipe dream. Any time you use one of the many great libraries out there, you end up with a project that cannot simply be checked out and worked on. You have to install all sorts of dependencies locally. The result is either a long check list of things that need to be cloned and installed before starting work, or the need to spend a lot of time creating scripts for bootstrapping your environment. This is unnecessary.</p>
<p>Why not simply run your own maven repository you might ask? I do, and I tend to share anything a put in there to the general public as well, but I shouldn't have to. And neither should anybody else. A private maven repository is a viable work around (because that is really what it is in my mind) for big companies and geeks like me. If you have the means and knowledge to do it. You need the dev-ops knowledge to do it, and you need the means to pay for virtual hosting to run it, since running a local maven repository doesn't add any benefits over simply using your local maven cache, you still have to install everything on each and every machine, and the whole point is to avoid that.</p>
<p>There is a very simple solution to this, but it involves all of the small time open source devs out there spending a tiny effort to make things easier for everybody. All you have to do is publish your libraries to a publicly available maven repository. Most of the big guys do it already. But even a lot of the big guys don't, which is a huge shame. The big guys aren't the people I want to reach here though, they can rest assured that their laziness makes me annoyed and I will voice my protests whenever I can. The guys I want to reach are you and I. The small guys. Publishing your tiny library on github. You rock. But you could rock even harder.</p>
<p>I get why you don't do it. You just want to get your software out there, and really don't care who uses it. I really get that. I'm the same. You might even think providing a maven repository is a lot of effort or will cost you something. It isn't, and it won't. You might think you need to be a dev-op and run your own nexus server. You don't. You can publish your project to the <a href="http://central.sonatype.org/">Sonatype Central Repository</a> with very little effort and no cost.</p>
<p>By spending a tiny once off effort, you will be saving all of us lot's of time and effort, and we will thank you for it. And if we all do it, we will all benefit as well. And don't think your library is to small or not important enough to do it. If you put it on github, somebody will use it and in my honest opinion, that all it takes. If your project is on github it is important enough that it should be in a repository.</p>
<p>So please, spend the extra hour of time to help save hours and hours of time for us all.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[dropwizard-jobs in public maven repository]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>As I previously mentioned I really really loathe having to install publicly available packages to my local <a href="https://gsmblog.net/tag/maven">maven</a> repository. While working on my personal <a href="https://gsmblog.net/tag/api">API</a> project, which I will return to in a later post once I've worked on it some more, I came across the need to use <a href="https://gsmblog.net/tag/quartz">quartz</a></p>]]></description><link>https://gsmblog.net/dropwizard-jobs-public-maven-repository/</link><guid isPermaLink="false">6037ddd40cae840001e946c6</guid><category><![CDATA[programming]]></category><category><![CDATA[dropwizard]]></category><category><![CDATA[guice]]></category><category><![CDATA[java]]></category><category><![CDATA[maven]]></category><category><![CDATA[quartz]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Fri, 01 May 2015 11:00:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/photo-1507481148557-c1cf8e9107d5.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/photo-1507481148557-c1cf8e9107d5.jpg" alt="dropwizard-jobs in public maven repository"><p>As I previously mentioned I really really loathe having to install publicly available packages to my local <a href="https://gsmblog.net/tag/maven">maven</a> repository. While working on my personal <a href="https://gsmblog.net/tag/api">API</a> project, which I will return to in a later post once I've worked on it some more, I came across the need to use <a href="https://gsmblog.net/tag/quartz">quartz</a> within <a href="https://gsmblog.net/tag/dropwizard">dropwizard</a> with <a href="https://gsmblog.net/tag/guice">guice</a> for dependency injection. I came across this nifty little library called <a href="https://github.com/spinscale/dropwizard-jobs">dropwizard-jobs</a>. Unfortunately as so many great projects I stumble across on github, the project isn't available in a public maven repository. So once again I decided to fork the project and publish it in my public maven repository.</p>
<p>This time around the project seems to be actively maintained, so I've reached out to the dev offering him access to the repository I created in case he wants to push to it himself. If I don't hear back from the dev I will not be actively adding anything to the project, but I will maintain the fork and repository with any updates that hit the original repository. If I forget, feel free to reach out by creating a ticket on github. If the original dev decides to use my repository, I will abandon the fork after the initial pull.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<p>The forked project is available at <a href="https://github.com/deadcyclo/dropwizard-jobs">https://github.com/deadcyclo/dropwizard-jobs</a></p>
<p>The public maven repository is available at <a href="https://nexus.vanntett.net/content/repositories/dropwizard-jobs/">https://nexus.vanntett.net/content/repositories/dropwizard-jobs/</a></p>
<p><small>Photo by <a href="https://unsplash.com/@tomkulczycki">Tom Kulczycki</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[fitbit4j - java fitbit api client in maven repository]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://github.com/deadcyclo/fitbit4j">fitbit4j</a> is a fully working <a href="https://gsmblog.net/tag/java">java</a> <a href="https://gsmblog.net/tag/api">API</a> client library for the fitbit-API. I currently started working on a project that I intent to integrate with <a href="https://www.fitbit.com/">fitbit</a> through their API, and quickly discovered a problem. The official fitbit4j api library is not available in <a href="https://gsmblog.net/tag/maven">maven</a>. However, fitbit pushed their maven releases</p>]]></description><link>https://gsmblog.net/fitbit4j-java-fitbit-api-client-maven-repository/</link><guid isPermaLink="false">6037ddd40cae840001e946c5</guid><category><![CDATA[programming]]></category><category><![CDATA[software]]></category><category><![CDATA[api]]></category><category><![CDATA[fitbit]]></category><category><![CDATA[java]]></category><category><![CDATA[maven]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Sat, 28 Mar 2015 15:13:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/photo-1510017803434-a899398421b3.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/photo-1510017803434-a899398421b3.jpg" alt="fitbit4j - java fitbit api client in maven repository"><p><a href="https://github.com/deadcyclo/fitbit4j">fitbit4j</a> is a fully working <a href="https://gsmblog.net/tag/java">java</a> <a href="https://gsmblog.net/tag/api">API</a> client library for the fitbit-API. I currently started working on a project that I intent to integrate with <a href="https://www.fitbit.com/">fitbit</a> through their API, and quickly discovered a problem. The official fitbit4j api library is not available in <a href="https://gsmblog.net/tag/maven">maven</a>. However, fitbit pushed their maven releases to <a href="http://github.com/">github</a>, which sort of works but is quite nasty. Unfortunately the latest version that they published in such a way was 1.0.25 which is broken.</p>
<p>Some time after fitbit published this version they changed their API to enforce <a href="http://en.wikipedia.org/wiki/Transport_Layer_Security">SSL</a>, which is a good thing. However, this broke version 1.0.25 of fitbit4j. They pushed code for version 1.0.26-SNAPSHOT to their public github repository, but did not release it in their &quot;maven repository&quot; on github. So the only solution, until now, if you wanted to use fitbit4j in your projects was to build it yourself and install it to your local maven cache. This is not a good solution. I loathe having artifacts installed to a local maven repository as this makes distributed work a pain and involves a manual checkout process, or potentially a scripted checkout process.</p>
<p>Since I loathe using local maven I naturally have my own maven repository running. I therefore decided to fork fitbit4j on github, change the publication setup and publish 1.0.26 as a release to my own fitbit maven repository. Now, since I have this repository online, I'll also share it to the world, so you too can avoid installing fitbit4j in your local maven cache, and rather use a proper repository.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<p>The forked project is available at <a href="https://github.com/deadcyclo/fitbit4j">https://github.com/deadcyclo/fitbit4j</a></p>
<p>The maven repository is available at <a href="https://nexus.vanntett.net/content/repositories/fitbit/">https://nexus.vanntett.net/content/repositories/fitbit/</a></p>
<p>Feel free to use this repository as much as you like. fitbit4j seems to have been abandoned after version 1.0.26, or possibly their API simply is very mature. If fitbit pushes updates to fitbit4j, I will sync them to the fork and publish new versions to the maven repository. This is not a true fork, hence I am not intending to maintain the project myself, other than syncing anything fitbit does, and naturally since I rely on the API-client myself, I will fix the client myself if it in the future breaks without a fix from fitbit themselves. I am not planning on adding any new features to the project. However, if you want to contribute any changes of fixes you might have implemented, feel free to submit a pull request and I'll include them after a code review (If this does happen, I will maintain a set of maven repositories, one for the official version and one for the version containing 3rd party contributions.</p>
<p>I also have a heads up if you plan on using fitbit4j to investigate the fitbit API: don't. fitbit4j is fairly large and messy, written in your typical enterprise bloated way, and not very well commented. If you want to use an API library to investigate their API, you are probably much better off having a look at <a href="https://github.com/orcasgit/python-fitbit">this third party python api library</a>. It is fairly small, tidy and well commented.</p>
<p><small>Photo by <a href="https://unsplash.com/@andresus">Andres Urena</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Date objects in Liferay Freemarker web content templates]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Have you ever tried to manipulate one of the web content meta data dates as an actual date in a <a href="https://gsmblog.net/tag/liferay">Liferay</a> <a href="https://gsmblog.net/tag/freemarker">Freemarker</a> web content template? Unfortunately these dates are handled very poorly by Liferay.</p>
<p>They are returned as a formatted string, based on the default locale of the portal, unless</p>]]></description><link>https://gsmblog.net/date-objects-liferay-freemarker-web-content-templates/</link><guid isPermaLink="false">6037ddd40cae840001e946c4</guid><category><![CDATA[programming]]></category><category><![CDATA[freemarker]]></category><category><![CDATA[java]]></category><category><![CDATA[velocity]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Thu, 30 Oct 2014 21:32:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/tEREUy1vSfuSu8LzTop3_IMG_2538.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/tEREUy1vSfuSu8LzTop3_IMG_2538.jpg" alt="Date objects in Liferay Freemarker web content templates"><p>Have you ever tried to manipulate one of the web content meta data dates as an actual date in a <a href="https://gsmblog.net/tag/liferay">Liferay</a> <a href="https://gsmblog.net/tag/freemarker">Freemarker</a> web content template? Unfortunately these dates are handled very poorly by Liferay.</p>
<p>They are returned as a formatted string, based on the default locale of the portal, unless you have manually changed the default locale of the structure the template uses. This means that the only way of obtaining the article create date, article publish date and last updated as actual date objects requires parsing strings, which in itself is a pain. Furthermore, since the date is formatted based on the used locale, you can't simply just parse the date either. Now in velocity you have access to a nice date utility that can parse a date string based on a provided locale. Unfortunately no such feature exists in Freemarker, so you have to change the actual locale of the template back and forth. Finally, not that this will only work if the default language of the structure the template is based on is the same as the default language of the portal. If they differ, your are simply out of luck as far as I have found out. If anybody knows how to solve this if these default locales differ, please, please let me know.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
# Retrieving dates as date objects example
Here is an example of retrieving the publish date of the web content in a Liferay Freemarker web content template.
<pre><code class="language-xml">&lt;#-- Retrieve the published date meta data field of the web content --&gt;
&lt;#assign displaydate = .vars['reserved-article-display-date'].data&gt;

&lt;#-- Save the original page locale for later --&gt;
&lt;#assign originalLocale = .locale&gt;

&lt;#-- Set the page locale to the portals default locale --&gt;
&lt;#setting locale = localeUtil.getDefault()&gt;

&lt;#-- Parse the date to a date object --&gt;
&lt;#assign displaydate = displaydate?datetime(&quot;EEE, d MMM yyyy HH:mm:ss Z&quot;)&gt;

&lt;#-- Set the page locale back to the original page locale --&gt;
&lt;#assign locale = originalLocale&gt;
</code></pre>
<p>Now you can use the date as a normal date object in freemarker:</p>
<pre><code class="language-xml">${displaydate?string[&quot;yyyy-MM-dd&quot;]}
</code></pre>
<h1 id="othermetadatadates">Other meta data dates</h1>
<p>The following three meta data dates are available on web content in Liferay:</p>
<pre><code class="language-xml">reserved-article-display-date
reserved-article-create-date
reserved-article-modified-date
</code></pre>
<p>So to use all three of theme, you could use the following code:</p>
<pre><code class="language-xml">&lt;#-- Retrieve the published date meta data field of the web content --&gt;
&lt;#assign displaydate = .vars['reserved-article-display-date'].data&gt;
&lt;#assign createdate = .vars['reserved-article-create-date'].data&gt;
&lt;#assign modifieddate = .vars['reserved-article-modified-date'].data&gt;

&lt;#-- Save the original page locale for later --&gt;
&lt;#assign originalLocale = .locale&gt;

&lt;#-- Set the page locale to the portals default locale --&gt;
&lt;#setting locale = localeUtil.getDefault()&gt;

&lt;#-- Parse the date to a date object --&gt;
&lt;#assign displaydate = displaydate?datetime(&quot;EEE, d MMM yyyy HH:mm:ss Z&quot;)&gt;
&lt;#assign createdate = createdate?datetime(&quot;EEE, d MMM yyyy HH:mm:ss Z&quot;)&gt;
&lt;#assign modifieddate = modifieddate?datetime(&quot;EEE, d MMM yyyy HH:mm:ss Z&quot;)&gt;

&lt;#-- Set the page locale back to the original page locale --&gt;
&lt;#assign locale = originalLocale&gt;
</code></pre>
<p>And you would use them all as normal, for example:</p>
<pre><code class="language-xml">${displaydate?date}
${createdate?time}
${modifieddate?iso_utc}
</code></pre>
<p><small>Photo by <a href="https://unsplash.com/@sonjalangford">Sonja Langford</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Liferay Spring MVC Freemarker Portlet Maven Archetype]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I've just published a <a href="https://gsmblog.net/tag/liferay">Liferay</a> <a href="https://gsmblog.net/tag/spring">Spring</a> <a href="https://gsmblog.net/tag/mvc">MVC</a> <a href="https://gsmblog.net/tag/freemarker">Freemarker</a> Portlet <a href="https://gsmblog.net/tag/maven">Maven</a> Archetype on github. It is a maven archetype for creating Liferay Spring MVC Freemarker portlets. Since this type of portlet is not officially supported by Liferay, no archetype currently exists. This archetype amends that. Furthermore this is a much more</p>]]></description><link>https://gsmblog.net/liferay-spring-mvc-freemarker-portlet-maven-archetype/</link><guid isPermaLink="false">6037ddd40cae840001e946c3</guid><category><![CDATA[programming]]></category><category><![CDATA[software]]></category><category><![CDATA[java]]></category><category><![CDATA[liferay]]></category><category><![CDATA[maven]]></category><category><![CDATA[jsp]]></category><category><![CDATA[spring]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Fri, 12 Sep 2014 18:55:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/photo-1490730141103-6cac27aaab94.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/photo-1490730141103-6cac27aaab94.jpg" alt="Liferay Spring MVC Freemarker Portlet Maven Archetype"><p>I've just published a <a href="https://gsmblog.net/tag/liferay">Liferay</a> <a href="https://gsmblog.net/tag/spring">Spring</a> <a href="https://gsmblog.net/tag/mvc">MVC</a> <a href="https://gsmblog.net/tag/freemarker">Freemarker</a> Portlet <a href="https://gsmblog.net/tag/maven">Maven</a> Archetype on github. It is a maven archetype for creating Liferay Spring MVC Freemarker portlets. Since this type of portlet is not officially supported by Liferay, no archetype currently exists. This archetype amends that. Furthermore this is a much more complete archetype than the archetypes currently supplied by Liferay. This archetype generates a runnable portlet including a lot of examples of best practices and the best way of creating a Liferay Spring MVC Freemarker portlet. These best practices are influenced by both Liferays best practices, but also the best practices of several projects I am currently working on.</p>
<p>Note that the setup allows one to use either <a href="https://gsmblog.net/tag/jsp">JSP</a> or Freemarker as views or a combination thereof. When looking for a view, first the system looks for a Freemarker template with the given name. If no Freemarker template is found, it goes on to look for a JSP template with the given name. If neither is found, the system fails. Hence it is possible to create portlets with a mixture of both Freemarker and JSP templates.</p>
<p>This is the initial version of the archetype. Improvements will be added whenever possible. If you have suggestions, ideas, or better practices I would be very happy to hear them, or even better receive a pull request.</p>
<p>The archetype is free (all I ask is that you contribute any improvements you make back) to use and located at <a href="https://github.com/deadcyclo/liferay-spring-mvc-freemarker-portlet-archetype">github</a>.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h1 id="requirementsforusingthemavenarchetype">Requirements for using the Maven Archetype</h1>
<p>To be able to use this archetype you need to download and install Liferay Portal EE Maven, if you are using the enterprise edition. If you are using the community edition, maven should be able to find this itself. Note currently I've been using SP5 version, since when I started working on this project, no artifacs were published for later service packs.</p>
<h1 id="installation">Installation</h1>
<ol>
<li>Clone the repository</li>
<li>Run the command <code>mvn install</code></li>
</ol>
<h1 id="usage">Usage</h1>
<ol>
<li>In your Liferay maven project run <code>mvn archetype:generate -DarchetypeCatalog=Local -Dfilter=com.liferay.maven.archetypes:</code></li>
<li>Select the number beside liferay-portlet-spring-mvc-freemarker-archetype</li>
<li>Continue as if you are using any of the archetypes provided by Liferay</li>
</ol>
<p><small>Photo by <a href="https://unsplash.com/@coopery">Mohamed Nohassi</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Samsung broke Galaxy Gear null_ ROM bluetooth tethering]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>It seems that <a href="https://gsmblog.net/tag/samsung">Samsung</a> has broken <a href="https://gsmblog.net/tag/bluetooth">Bluetooth</a> tethering on their latest Galaxy Gear update for the Galaxy Note III, even when using the <a href="http://nullproject.net/">null_ ROM</a>. But I have a solution. Are you having issues with extremely slow downloads and multiple time outs. Does it take ages to download from the</p>]]></description><link>https://gsmblog.net/samsung-broke-galaxy-gear-null_-rom-bluetooth-tethering/</link><guid isPermaLink="false">6037ddd40cae840001e946c2</guid><category><![CDATA[mobile]]></category><category><![CDATA[android]]></category><category><![CDATA[bluetooth]]></category><category><![CDATA[samsung]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Wed, 26 Mar 2014 21:23:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/photo-1509662510351-e023cc2b59a1-adjusted.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/photo-1509662510351-e023cc2b59a1-adjusted.jpg" alt="Samsung broke Galaxy Gear null_ ROM bluetooth tethering"><p>It seems that <a href="https://gsmblog.net/tag/samsung">Samsung</a> has broken <a href="https://gsmblog.net/tag/bluetooth">Bluetooth</a> tethering on their latest Galaxy Gear update for the Galaxy Note III, even when using the <a href="http://nullproject.net/">null_ ROM</a>. But I have a solution. Are you having issues with extremely slow downloads and multiple time outs. Does it take ages to download from the Play store, or can't you download from the <a href="http://en.wikipedia.org/wiki/Google_Play">Play store</a> at all? Read on.</p>
<h1 id="theproblem">The problem</h1>
<p>I installed the latest update a few days ago, and today I needed to install some additional software on the Galaxy Gear. I simply could not get the Play store to work. It loaded and loaded, but never got anywhere. One single time I got it to start downloading, but after a half an hour the download of 2 MB was still at 3%. I tried absolutely everything, but could not get it working. I tried disconnecting my Bluetooth keyboard and mouse, thinking that they somehow were saturating the Bluetooth link. I tried connecting to multiple other phones and computers. I tried rebooting everything, multiple times. I tried clearing settings. I tried disconnecting and re-pairing devices. I even tried updating to the latest version of the null_ ROM. But no matter what I did, the issue prevailed. I googled for hours, with no solution found. However, I saw a lot of people complaining about this exact problem. I also so a lot of people answering that &quot;Well, Bluetooth tethering is slow. This is ordinary.&quot; which, pardon my French, is complete BS. Yes Bluetooth tethering is slower than WIFI, but it is not that slow. You aren't exactly able do to HD-streaming, but downloading from the Play store, and surfing the web works quite fine (like it used to do before the last Galaxy Gear software update from Samsung. This kind of bad performance is quite obviously due to something being broken, and anybody who says different is not telling you the truth.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h1 id="thesolution">The solution</h1>
<p>I was about to give up, when I finally stumbled upon a solution. First I paired my Galaxy Gear with my <a href="http://en.wikipedia.org/wiki/Samsung_Galaxy">Samsung Galaxy</a> S4 which does not have Samsung Galaxy Gear software installed on it. Next I turned off Bluetooth on my Galaxy Note III to ensure that the Galaxy Gear disconnected from it. Finally I activated Bluetooth tethering on my S4 and on the Galaxy Gear. Suddenly, working Bluetooth tethering with even better speeds than I was experiencing on the Note III prior to the latest Samsung Galaxy Gear software update. So there you have it. To get Bluetooth tethering working on the Galaxy Gear running the null_ ROM, you have to disconnect it from all devices running Samsung Galaxy Gear software, pair it with a device not running said software, and activate Bluetooth tethering.</p>
<h1 id="intentional">Intentional?</h1>
<p>I'm really hoping this wasn't intentionally done by Samsung, but I find it very strange that this feature, that worked fine when using previous versions of their software suddenly stopped working. Specially considering how much effort they have put down in only allowing authorized applications to use the Bluetooth link when running their stock firmware.</p>
<p><small>Photo by <a href="https://unsplash.com/@seteales">Allef Vinicius</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Spark framework: The tiny framework that almost could]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>As part of my complete makeover of all my websites I spent the last few weeks creating an application in <a href="http://sparkjava.com/">sparkframework</a>, a non-enterprise <a href="https://gsmblog.net/tag/java">Java</a> micro web framework. However, I never actually launched it. I came very close, and I'll get back to why not in a moment.</p>
<h1 id="thesparkframework">The spark framework</h1>]]></description><link>https://gsmblog.net/spark-framework-the-tiny-framework-that-almost-could/</link><guid isPermaLink="false">6037ddd40cae840001e946c1</guid><category><![CDATA[programming]]></category><category><![CDATA[tutorial]]></category><category><![CDATA[apache]]></category><category><![CDATA[bootstrap]]></category><category><![CDATA[freemarker]]></category><category><![CDATA[java]]></category><category><![CDATA[jetty]]></category><category><![CDATA[json]]></category><category><![CDATA[jsp]]></category><category><![CDATA[spring]]></category><category><![CDATA[varnish]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Tue, 04 Mar 2014 21:37:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/photo-1478088702756-f16754aaf0c4.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/photo-1478088702756-f16754aaf0c4.jpg" alt="Spark framework: The tiny framework that almost could"><p>As part of my complete makeover of all my websites I spent the last few weeks creating an application in <a href="http://sparkjava.com/">sparkframework</a>, a non-enterprise <a href="https://gsmblog.net/tag/java">Java</a> micro web framework. However, I never actually launched it. I came very close, and I'll get back to why not in a moment.</p>
<h1 id="thesparkframework">The spark framework</h1>
<p>The spark framework is a tiny non-enterprise Java framework that gives you the bare minimum needed to create a web application, with just a few lines of code, and not much more. It provides embedded <a href="https://gsmblog.net/tag/jetty">Jetty</a> out of the box, so you can use it without rolling out your own app server, which is nice. It also supports <a href="https://gsmblog.net/tag/freemarker">Freemarker</a>-, and <a href="https://gsmblog.net/tag/jsp">JSP</a>-templates. In other words you can assume it's great if you either like building everything from scratch yourself (and actually have time), or you are creating something very simple.</p>
<p>Just to give an impression of how minimalistic it actually is, and what it provides, here are the features:</p>
<ul>
<li>Routes</li>
<li>Request</li>
<li>Query maps</li>
<li>Response</li>
<li>Cookies</li>
<li>Session management</li>
<li>Halting</li>
<li>Filters</li>
<li>Browser Redirect</li>
<li>Static files (in package and/or external)</li>
<li>Freemarker and JSP templates with views</li>
</ul>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h1 id="myapplication">My application</h1>
<p>And this sounded to me to be exactly what I needed. What I wanted to do was replace a whole bunch of sites with a single tiny small Java application. The sites in question are currently based on static html, <a href="http://en.wikipedia.org/wiki/Server_Side_Includes">Server Side Includes</a> and a lot of HTTP redirects in <a href="https://gsmblog.net/tag/apache">Apache</a>, so a micro framework should have been exactly what I needed. I spent a couple of weekends creating a very minimalistic application to handle this. The main class is only about 30 lines of Java code, supported by a few POJOs. In addition, the application included a whole bunch of Freemarker templates, three <a href="https://gsmblog.net/tag/json">JSON</a> files, and a few <a href="https://gsmblog.net/tag/bootstrap">bootstrap</a> 3 themes. Each page are rendered through a master template for the given site, and the page template included through it. I also included a fall back for unknown sites letting me render a &quot;oops&quot; site for any incoming requests to the sever that doesn't map to a known site. This allows the application to act as a &quot;catch all&quot; backend for <a href="https://gsmblog.net/tag/varnish">Varnish</a>.</p>
<p>The three JSON files define the structure of the sites. One file contains entries for each page of each site including sub-template to be used, title, path and any other site specific settings or content that are used across multiple pages. The second file contains redirects and the third JSON file simply contains a lists of all of my sites, since I want to include that list on multiple sites but maintain in from a single location.</p>
<p>Adding a new page to a site simply involves adding a new Freemarker template, and giving the page an entry in the first JSON file. Easy as can be. Adding a new redirect simply involves adding an entry to a JSON file. Finally, adding a new site involves adding new entries to the first JSON file and adding a couple of lines of Java code. The extra couple of lines of Java code should really not be necessary, and I was planning on changing this soon, until the problem arose.</p>
<h1 id="theproblem">The problem</h1>
<p>Right before launch I discovered a problem, that ended up thwarted my whole effort. While testing the sites before switching DNS entries, I discovered that some 404 errors where rendered by a boring generic jetty 404 page. This was not my intention. My initial thought was to create a top level &quot;splat&quot; route at the lowest priority level, to trap any incoming non-recognized requests and spit out a custom 404 based on the site. But that broke static files, since routes have higher priority that static files no matter what in the spark framework. How about a filter? Include a top level end filter that checks for 404s and redirects based on that? Nope. The exit filter obviously also is applied before the static route. What about adding a custom 404 page to jetty? Not possible. The spark framework doesn't provide any customization of the built in Jetty server.</p>
<p>Leaving the only option to not use the embedded jetty but rather deploy the application on an external app server, which completely screws up the whole idea of the application. If that is the only option, why not go for <a href="https://gsmblog.net/tag/spring">Spring</a> and get all of the Spring magic? Or simply make the whole thing from scratch based on an embedded Jetty? The whole idea of the project was a simple drop and run jar without any external dependencies, configuration or effort. Naturally, what I really should do is fork the project and add the option myself, but with my current TODO-list that probably will not happen for a good 10 years or so.</p>
<p>So there you have it. Custom 404 pages combined with static content and embedded Jetty in simply not possible in the spark framework which is a shame. So now I'm going to spend time redoing the project in <a href="http://projects.spring.io/spring-boot/">Spring Boot</a> instead.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h1 id="conclusion">Conclusion</h1>
<p>The spark framework is a really cool concept. I really like the idea of being able to create non-enterprise applications with minimal a minimal footprint in Java. However, the lack of possibility to configure the embedded Jetty server is a big show stopper as far as I am concerned. Until that is fixed, I'll probably not be using the spark framework and rather making do with the &quot;enterprizy&quot; Spring Boot framework instead.</p>
<p>However, if you really like making everything from scratch yourself (I really do, but I've discovered that I never finish anything I try to make from scratch, so I've more or less given up on not using frameworks), and like non-enterprise Java (yeah, it really does exist), you most definitely want to look into the spark framework. And hopefully you like tinkering, and decide that you should contribute configuration of the embedded Jetty server to the project (I really wish I had the time).</p>
<p>Finally, spark is probably really great for prototyping and creating proofs of concept, and I'll probably be using it for that in the future.</p>
<p><small>Photo by <a href="https://unsplash.com/@bhushan07">Bhushan Sadani</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Liferay Spring MVC portlets]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Here I will guide you step by step through the process of creating Liferay Spring MVC portlets. I've already showed you <a href="https://gsmblog.net/liferay-freemarker-spring-mvc-portlets/">how to create Liferay Spring MVC Freemarker portlets</a> and <a href="https://gsmblog.net/liferay-spring-mvc-freemarker-and-jsp-portlets/">how to create Liferay Spring MVC Freemarker and JSP portlets</a>. However, both of those posts require the prior knowledge of</p>]]></description><link>https://gsmblog.net/liferay-spring-mvc-portlets/</link><guid isPermaLink="false">6037ddd40cae840001e946c0</guid><category><![CDATA[programming]]></category><category><![CDATA[tutorial]]></category><category><![CDATA[java]]></category><category><![CDATA[liferay]]></category><category><![CDATA[mvc]]></category><category><![CDATA[spring]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Tue, 18 Feb 2014 22:54:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/fN6hZMWqRHuFET5YoApH_StBalmainCoffee.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/fN6hZMWqRHuFET5YoApH_StBalmainCoffee.jpg" alt="Liferay Spring MVC portlets"><p>Here I will guide you step by step through the process of creating Liferay Spring MVC portlets. I've already showed you <a href="https://gsmblog.net/liferay-freemarker-spring-mvc-portlets/">how to create Liferay Spring MVC Freemarker portlets</a> and <a href="https://gsmblog.net/liferay-spring-mvc-freemarker-and-jsp-portlets/">how to create Liferay Spring MVC Freemarker and JSP portlets</a>. However, both of those posts require the prior knowledge of how to create <a href="https://gsmblog.net/tag/liferay">Liferay</a> <a href="https://gsmblog.net/tag/spring">Spring</a> <a href="https://gsmblog.net/tag/mvc">MVC</a> portlets. There are already several resources online that describe how to do this, but I find them either too verbose, or faulty, so I'm writing my own hoping to fix this.</p>
<p>Personally I find Spring MVC portlets highly superior to Liferay MVC portlets in many different ways. You can easily take advantage of spring injections. You use spring annotations for render requests, action requests and resource requests which, if you do it right, results in much more structured controllers with much less boiler plate code that are much more readable to human beings. Still doing things right, your whole project is more structured, organized and logical. You can also use Freemarker instead of JSP preferable. If you don't know why you should read <a href="http://blog.stackhunter.com/2014/01/17/10-reasons-to-replace-your-jsps-with-freemarker-templates/">10 reasons to replace your JSPs with Freemarker templates</a> on the Stack Hunter blog. Finally, your portlets end up much more maintainable simply because there are a whole lot more programmers out there that know Spring compared to the amount of programmers who know Liferay MVC.</p>
<p>Now, Spring MVC portlets might be great, and I highly recommend them if you are creating traditional portlets. However, if you need to create webapp portlets, you probably shouldn't be using Spring MVC. For webapp portlets, I recommend using Vaadin if you are creating a business application. If you are creating a non-business application, my preference is AngularJS, possibly combined with Spring MVC. I'll be writing more about both Vaadin and AngularJS in future posts.</p>
<p>Unfortunately Liferay doesn't provide us a means of creating Spring MVC portlets out of the box in their IDE or Developer Studio. I'm still hoping that they soon add this feature, and also fix the issue with Spring MVC Freemarker portlets that I show a work around for in one of my previous posts mentioned above. However, their new Maven support is a great feature though still a bit rough around the edges and Liferay 6.2 is a huge step forward. If anybody else created Liferay 6.2 it would be given the name Liferay Next Gen or something like that.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h1 id="step1creatingtheportlet">Step 1: Creating the portlet</h1>
<p>To be able to create a Spring MVC portlet in Liferay, you first need to create a regular Liferay MVC portlet using the Liferay SDK, and then manually modifying it to become a Spring MVC portlet. Do this however you like, either by using the command line tool in the SDK or in your favourite IDE.</p>
<h1 id="step2turnyourportletclassintospringdispatcherportlet">Step 2: Turn your portlet class into Spring DispatcherPortlet</h1>
<p>Open the portlet.xml file in your docroot/WEB-INF folder and replace</p>
<pre><code class="language-xml">&lt;portlet-class&gt;com.liferay.util.bridges.mvc.MVCPortlet&lt;/portlet-class&gt;
</code></pre>
<p>with</p>
<pre><code class="language-xml">&lt;portlet-class&gt;org.springframework.web.portlet.DispatcherPortlet&lt;/portlet-class&gt;
</code></pre>
<h1 id="step3createaspringapplicationcontextfile">Step 3: Create a Spring application context file</h1>
<p>Now you need to create a spring application context file. Personally I prefer calling this file nameofyourportlet-portlet.xml in lower case rather than applicationContext.xml, since this makes it easier to add multiple portlets to a single portlet project without collisions. The file should be created in docroot/WEB-INF (the same place as your portlet.xml file) and should contain the following:</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;  
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;  
     xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:p=&quot;http://www.springframework.org/schema/p&quot;  
     xmlns:context=&quot;http://www.springframework.org/schema/context&quot;  
     xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot;  
     xsi:schemaLocation=&quot;  
            http://www.springframework.org/schema/beans  
            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd  
            http://www.springframework.org/schema/context  
            http://www.springframework.org/schema/context/spring-context-3.1.xsd  
            http://www.springframework.org/schema/aop  
      http://www.springframework.org/schema/aop/spring-aop-2.5.xsd&quot;&gt;  
  &lt;context:annotation-config /&gt;  
  &lt;bean class=&quot;org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping&quot; /&gt;  
&lt;/beans&gt;
</code></pre>
<h1 id="step4tellingtheportletclasswheretofindthespringapplicationcontex">Step 4: Telling the portlet class where to find the spring application contex</h1>
<p>Back in your portlet.xml file located in docroot/WEB-INF replace</p>
<pre><code class="language-xml">&lt;init-param&gt;  
  &lt;name&gt;view-jsp&lt;/name&gt;  
  &lt;value&gt;/view.jsp&lt;/value&gt;  
&lt;/init-param&gt;
</code></pre>
<p>with</p>
<pre><code class="language-xml">&lt;init-param&gt;  
  &lt;name&gt;contextConfigLocation&lt;/name&gt;  
  &lt;value&gt;/WEB-INF/nameofyourportlet-portlet.xml&lt;/value&gt; 
&lt;/init-param&gt;
</code></pre>
<p>ensuring that you use the file name of the file you created in step 3.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h1 id="step5addingspringjarstoproject">Step 5: Adding Spring jars to project</h1>
<p>Find and open the liferay-plugin-package.properties file in docroot/WEB-INF and add the following to the end of the file:</p>
<pre><code>portal-dependency-jars=\
    spring-web-servlet.jar,\
    spring-web-portlet.jar,\
    spring-web.jar,\
    spring-transaction.jar,\
    spring-jdbc.jar,\
    spring-expression.jar,\
    spring-core.jar,\
    spring-context.jar,\
    spring-beans.jar,\
    spring-asm.jar,\
    spring-aop.jar,\
    commons-beanutils.jar,\
    commons-collections.jar,\
    commons-fileupload.jar,\
    commons-io.jar,\
    commons-lang.jar,\
    jstl-api.jar,\
    spring-context-support.jar,\
    jstl-impl.jar,\
</code></pre>
<p>The jstl files are optional, but I highly recommend using jstl when you are forced to use JSP, so I included them here. Also note that if you are going to be using Freemarker, you need to include the freemarker.jar file here as well.</p>
<p>At this point you should build your project. Depending upon what IDE you are using, you might need to manually add the jar files to the projects classpath as well.</p>
<h1 id="step6addviewrenderservletentryinthewebxmlfile">Step 6: Add ViewRenderServlet entry in the web.xml file</h1>
<p>In the web.xml file located in docroot/WEB-INF add the following entry:</p>
<pre><code class="language-xml">&lt;servlet&gt;  
  &lt;servlet-name&gt;view-servlet&lt;/servlet-name&gt;  
  &lt;servlet-class&gt;org.springframework.web.servlet.ViewRendererServlet&lt;/servlet-class&gt;  
  &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;  
&lt;/servlet&gt;  
&lt;servlet-mapping&gt;  
  &lt;servlet-name&gt;view-servlet&lt;/servlet-name&gt;  
  &lt;url-pattern&gt;/WEB-INF/servlet/view&lt;/url-pattern&gt;  
&lt;/servlet-mapping&gt;
</code></pre>
<h1 id="step7addspringviewresolver">Step 7: Add Spring view resolver</h1>
<p>Spring supports many different types of view resolvers. Personally I prefer the Freemarker resolver when creating Liferay MVC portlets. If you want to use Freemarker, you should read the post I linked to at the beginning of this post. Also note that with Spring 3.0 or higher you can use multiple prioritized view resolvers, again see the links at the beginning of this post.</p>
<p>In the application context file you created in step 3 add the following entry:</p>
<pre><code class="language-xml">&lt;bean id=&quot;jspViewResolver&quot; class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&gt;  
  &lt;property name=&quot;viewClass&quot; value=&quot;org.springframework.web.servlet.view.InternalResourceView&quot; /&gt;  
  &lt;property name=&quot;prefix&quot; value=&quot;/WEB-INF/jsp/&quot; /&gt;  
  &lt;property name=&quot;suffix&quot; value=&quot;.jsp&quot; /&gt;  
  &lt;property name=&quot;order&quot; value=&quot;1&quot; /&gt;  
&lt;/bean&gt;
</code></pre>
<p>This ensures that what ever you return form a Spring @RenderMapping annotated controller function will be resolved to a JSP file in docroot/WEB-INF/jsp/. So you if for example return &quot;myview&quot; from your controller render function, Spring will use the file docroot/WEB-INF/jsp/myview.jsp to render your portlet.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<h1 id="step7createacontrollerwitharenderrequesthandler">Step 7: Create a controller with a render request handler</h1>
<p>Here we create a very simple Spring controller that adds a simple request handler to render the jsp file mentioned above. This is not an exhaustive Spring tutorial, and if you don't know Spring MVC, you should find a good tutorial explaining controllers, render requests, action requests, resource requests, etc, and read it after completing this tutorial.</p>
<pre><code class="language-java">package is.brendan.liferay.portlet.testportlet.controller;  
import javax.portlet.RenderRequest;  
import javax.portlet.RenderResponse;  
import org.springframework.stereotype.Controller;  
import org.springframework.ui.Model;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.portlet.bind.annotation.RenderMapping;  

@Controller(value = &quot;MyController&quot;)  
@RequestMapping(&quot;VIEW&quot;)  
public class MyController {

  @RenderMapping  
  public String handleRenderRequest(RenderRequest request,RenderResponse response,Model model){  

    return &quot;myview&quot;;  
  }  
}
</code></pre>
<h1 id="step8definethecontrollerintheapplicationcontext">Step 8: Define the controller in the application context</h1>
<p>In your application context file created in step 3 add the following:</p>
<pre><code class="language-xml">&lt;bean class=&quot;is.brendan.liferay.portlet.controller.MyController&quot; /&gt;
</code></pre>
<p>Congratulations. If you did everything right, you have now created a fully functioning Liferay Spring MVC portlet.</p>
<p><small>Photo by <a href="https://unsplash.com/@carlijeen">Carli Jeen</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Liferay Spring MVC Freemarker and JSP portlets]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I previously <a href="https://gsmblog.net/liferay-freemarker-spring-mvc-portlets/">showed how to create a Liferay Spring MVC Freemarker portlets</a>. But what if you want to use both <a href="https://gsmblog.net/tag/freemarker">Freemarker</a> and <a href="https://gsmblog.net/tag/jsp">JSP</a> in the same portlet? Is it possible? If you are using <a href="https://gsmblog.net/tag/spring">Spring</a> 3.0 or higher, it certainly is. And it's actually very simple and easy to</p>]]></description><link>https://gsmblog.net/liferay-spring-mvc-freemarker-and-jsp-portlets/</link><guid isPermaLink="false">6037ddd40cae840001e946bf</guid><category><![CDATA[programming]]></category><category><![CDATA[tutorial]]></category><category><![CDATA[freemarker]]></category><category><![CDATA[java]]></category><category><![CDATA[jsp]]></category><category><![CDATA[liferay]]></category><category><![CDATA[mvc]]></category><category><![CDATA[spring]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Tue, 18 Feb 2014 21:17:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2018/01/5cde60f6.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2018/01/5cde60f6.jpg" alt="Liferay Spring MVC Freemarker and JSP portlets"><p>I previously <a href="https://gsmblog.net/liferay-freemarker-spring-mvc-portlets/">showed how to create a Liferay Spring MVC Freemarker portlets</a>. But what if you want to use both <a href="https://gsmblog.net/tag/freemarker">Freemarker</a> and <a href="https://gsmblog.net/tag/jsp">JSP</a> in the same portlet? Is it possible? If you are using <a href="https://gsmblog.net/tag/spring">Spring</a> 3.0 or higher, it certainly is. And it's actually very simple and easy to do. I'm going to show you how to create <a href="https://gsmblog.net/tag/liferay">Liferay</a> Spring <a href="https://gsmblog.net/tag/mvc">MVC</a> Freemarker and JSP portlets in a moment.</p>
<p>But first. When would you need to do this? I really haven't found any use for this when creating a brand new portlet on my own. However, I have found this useful in multiple cases. If somebody delivers JSP-files for a project you are working on, and you really want to do your own work in Freemarker, since Freemarker is superior to JSP, this gives you a solution without very much added effort. This method is also great when upgrading your portlets from JSP to Freemarker. It allows you to gradually upgrade rather than do everything in a single big bang. And if you aren't really sure if you should be upgrading your portlets or not, I highly recommend reading <a href="http://blog.stackhunter.com/2014/01/17/10-reasons-to-replace-your-jsps-with-freemarker-templates/">10 reasons to replace your JSPs with Freemarker templates</a> on the Stack Hunter blog. Finally, if you are extending a legacy portlet that uses JSP, and you really don't feel like mucking about with JSP any more, this method comes to the rescue.</p>
<p>Now, before you continue, Liferay has an issue with Spring MVC Freemarker portlets resulting in a nullpointer exception in FreeMarkerView.getTemplate(). If you don't already know how to handle this, please read my post I linked to in the first paragraph before continuing.</p>
<p>Spring 3.0 added support for multiple view resolvers by giving them priorities. Using multiple ordered view resolvers is as simple as adding both resolvers, just as you would if you were adding theme as singletons in addition to giving them a priority. View resolvers with a lower priority number resolve before resolvers with a higher priority number.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<p>Contemplate this code:</p>
<pre><code class="language-xml">&lt;bean id=&quot;freemarkerConfig&quot; class=&quot;org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer&quot;&gt;
  &lt;property name=&quot;templateLoaderPath&quot; value=&quot;/WEB-INF/freemarker/&quot;/&gt;
&lt;/bean&gt;

&lt;bean id=&quot;viewResolver&quot; class=&quot;is.brendan.liferay.web.freemarker.ExtendedFreemarkerViewResolver&quot;&gt;
  &lt;property name=&quot;cache&quot; value=&quot;true&quot;/&gt;
  &lt;property name=&quot;prefix&quot; value=&quot;&quot;/&gt;
  &lt;property name=&quot;suffix&quot; value=&quot;.ftl&quot;/&gt;
  &lt;property name=&quot;exposeSpringMacroHelpers&quot; value=&quot;true&quot;/&gt;
  &lt;property name=&quot;freemarkerConfigurer&quot; ref=&quot;freemarkerConfig&quot;/&gt;
  &lt;property name=&quot;order&quot; value=&quot;0&quot; /&gt;
&lt;/bean&gt;

&lt;bean id=&quot;jspViewResolver&quot;  
  class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&gt;  
  &lt;property name=&quot;viewClass&quot;  
   value=&quot;org.springframework.web.servlet.view.InternalResourceView&quot; /&gt;  
  &lt;property name=&quot;prefix&quot; value=&quot;/WEB-INF/jsp/&quot; /&gt;  
  &lt;property name=&quot;suffix&quot; value=&quot;.jsp&quot; /&gt;  
  &lt;property name=&quot;order&quot; value=&quot;1&quot; /&gt;  
 &lt;/bean&gt;
</code></pre>
<p>Now, if you return the &quot;myview&quot; in your Spring MVC @RenderMapping mapped method, Spring will first investigate the /WEB-INF/freemarker/ folder looking for a file named myview.ftl. If that file does not exist, Spring will move on and investigate the /WEB-INF/jsp/ folder looking for a file named myview.jsp. Naturally, if neither of the files exists, your portlet will crash and burn.</p>
<p><small>Photo by <a href="https://unsplash.com/@glencarrie">Glen Carrie</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Liferay Freemarker Spring MVC portlets]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p><a href="https://gsmblog.net/tag/liferay">Liferay</a> <a href="https://gsmblog.net/tag/freemarker">Freemarker</a> <a href="https://gsmblog.net/tag/spring">Spring</a> <a href="https://gsmblog.net/tag/mvc">MVC</a> <a href="https://gsmblog.net/tag/portlet">portlets</a> are great, and certainly are my favourite way of developing portlets in Liferay at the moment, when creating non-webapp portlets. For business applications created as webapps Vaadin is most certainly the way to go, and for non-business applications created as webapps, my personal favourite at</p>]]></description><link>https://gsmblog.net/liferay-freemarker-spring-mvc-portlets/</link><guid isPermaLink="false">6037ddd40cae840001e946be</guid><category><![CDATA[programming]]></category><category><![CDATA[tutorial]]></category><category><![CDATA[freemarker]]></category><category><![CDATA[intellij]]></category><category><![CDATA[java]]></category><category><![CDATA[liferay]]></category><category><![CDATA[spring]]></category><category><![CDATA[mvc]]></category><dc:creator><![CDATA[Brendan Johan Lee]]></dc:creator><pubDate>Tue, 18 Feb 2014 20:24:00 GMT</pubDate><media:content url="https://gsmblog.net/content/images/2017/12/photo-1444418185997-1145401101e0.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://gsmblog.net/content/images/2017/12/photo-1444418185997-1145401101e0.jpg" alt="Liferay Freemarker Spring MVC portlets"><p><a href="https://gsmblog.net/tag/liferay">Liferay</a> <a href="https://gsmblog.net/tag/freemarker">Freemarker</a> <a href="https://gsmblog.net/tag/spring">Spring</a> <a href="https://gsmblog.net/tag/mvc">MVC</a> <a href="https://gsmblog.net/tag/portlet">portlets</a> are great, and certainly are my favourite way of developing portlets in Liferay at the moment, when creating non-webapp portlets. For business applications created as webapps Vaadin is most certainly the way to go, and for non-business applications created as webapps, my personal favourite at the moment is AngularJS which plays very nicely with Liferay. I'll get back to Vaadin and AngularJS in later posts.</p>
<p>Now creating Spring MVC portlets in Liferay is common, and fairly well documented all over the place on the net. It does involve a manual process where you start out with a Liferay MVC portlet, and change it to a Spring MVC portlet. I'm hoping Liferay soon will realize the value of adding Spring MVC portlets to their SDK, so we avoid having to do this manually. Intellij support would be nice as well, but their new Maven support in 6.2 goes a long way towards mitigating the problem, though it still is a bit flaky in the current version of the new Liferay IDE I'm using.</p>
<p>Spring MVC portlets are great, but you still have to deal with JSP. For the real killer combination, you need to add Freemarker to the mix. Unfortunately, though using Freemarker in Spring MVC is very straight forward, using it in a Liferay portlet is not. The issue manifests itself as a nullpointer exception in FreeMarkerView.getTemplate(FreeMarkerView.java) This &quot;bug&quot; has existed in all versions of Liferay I have worked with, and has yet to be fixed in Liferay 6.2. Hopefully as Freemarker becomes more and more popular, Liferay will finally decide to fix this issue. Luckily there is a workaround which I will describe here. This works in Liferay 6.1.1+ and Liferay 6.2. For earlier versions a different workaround exists, but I will not describe it here, as you really should have moved on from Liferay 6.0 and 6.1.0 by now.</p>
<div class="ad-header">Article continues after ad</div>
<div class="inline-ad">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-4420100971103553" data-ad-slot="6084677805"></ins>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>
<p>The first step of the workaround is to create the following Java class:</p>
<pre><code class="language-java">package is.brendan.liferay.web.freemarker;
import java.lang.reflect.Method;
import javax.portlet.PortletContext;
import javax.servlet.ServletContext;
import org.springframework.web.portlet.context.PortletContextAware;
import org.springframework.web.servlet.view.AbstractUrlBasedView;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;

public class ExtendedFreemarkerViewResolver extends FreeMarkerViewResolver implements PortletContextAware {
        private FreeMarkerConfigurer freemarkerConfigurer;

        @Override
        protected AbstractUrlBasedView buildView(String viewName) throws Exception {
                FreeMarkerView freemarkerView = (FreeMarkerView)super.buildView(viewName);

                freemarkerView.setConfiguration(freemarkerConfigurer.getConfiguration());
                freemarkerView.setServletContext(getServletContext());

                return freemarkerView;
        }

        public void setFreemarkerConfigurer(FreeMarkerConfigurer freemarkerConfigurer) {
                this.freemarkerConfigurer = freemarkerConfigurer;
        }

        @Override
        public void setPortletContext(PortletContext context) {
                try {
                        Method meth = context.getClass().getDeclaredMethod(&quot;getServletContext&quot;);
                        setServletContext((ServletContext)meth.invoke(context));
                } catch (Exception e) {
                        throw new UnsupportedOperationException(&quot;Could not get servletcontext&quot;, e);
                }
        }

}
</code></pre>
<p>Next, in your applicationcontext do the following:</p>
<pre><code class="language-xml">&lt;bean id=&quot;freemarkerConfig&quot; class=&quot;org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer&quot;&gt;
  &lt;property name=&quot;templateLoaderPath&quot; value=&quot;/WEB-INF/freemarker/&quot;/&gt;
&lt;/bean&gt;

&lt;bean id=&quot;viewResolver&quot; class=&quot;is.brendan.liferay.web.freemarker.ExtendedFreemarkerViewResolver&quot;&gt;
  &lt;property name=&quot;cache&quot; value=&quot;true&quot;/&gt;
  &lt;property name=&quot;prefix&quot; value=&quot;&quot;/&gt;
  &lt;property name=&quot;suffix&quot; value=&quot;.ftl&quot;/&gt;
  &lt;property name=&quot;exposeSpringMacroHelpers&quot; value=&quot;true&quot;/&gt;
  &lt;property name=&quot;freemarkerConfigurer&quot; ref=&quot;freemarkerConfig&quot;/&gt;
&lt;/bean&gt;
</code></pre>
<p>Now you are good to go. Drop your Freemarker templates in <code>/WEB-INF/freemarker/</code> and use them just like you would in a regular Spring MVC portlet.</p>
<p>And if you want to get really fancy, you can add the ability to load Freemarker templates from outside of your portlet project for added power. This allows you to allow your portlet to use Freemarker templates uploaded through the Liferay Sync application. Check out this <a href="https://github.com/monator/freemarker-template-loader-samples">proof of concept</a>. Though this is a rather neat feature, if you already have moved to Liferay 6.2 you are probably going to want to use the new <a href="http://www.liferay.com/documentation/liferay-portal/6.2/user-guide/-/ai/using-application-display-templates-liferay-portal-6-2-user-guide-07-en">Liferay Application Display Template</a> feature since it's fully supported by Liferay, and not a hack like the proof of concept mentioned above.</p>
<p><small>Photo by <a href="https://unsplash.com/@frankieis">frankie</a> / <a href="https://unsplash.com/">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>