<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Green's Opinion</title>
    <description>Green's Opinion provides insight and opinions on software and open source.</description>
    <link>https://www.greensopinion.com/</link>
    <atom:link href="https://www.greensopinion.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 14 Jan 2022 12:36:29 -0800</pubDate>
    <lastBuildDate>Fri, 14 Jan 2022 12:36:29 -0800</lastBuildDate>
    <generator>Jekyll v3.8.5</generator>
    
      <item>
        <title>Flutter Maps With Vector Tiles</title>
        <description>&lt;p&gt;Flutter is a huge productivity booster except for a few sharp edges, one of which is embedding maps. Maps are commonly added using the &lt;a href=&quot;https://pub.dev/packages/google_maps_flutter&quot;&gt;google_maps_flutter&lt;/a&gt; package, which uses native platform widgets to embed a Google map in your Flutter app. The resulting map looks and behaves beautifully, except when it doesn’t. The platform/Flutter combination is not perfect, and for my use-case isn’t good enough for a prodution app. This post details the issues that I encountered and how I ended up solving the problem by creaing a plug-in &lt;a href=&quot;https://pub.dev/packages/vector_map_tiles&quot;&gt;vector_map_tiles&lt;/a&gt; to add support for vector tiles to &lt;a href=&quot;https://pub.dev/packages/flutter_map&quot;&gt;flutter_map&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There were two issues that prevented me from using Google maps in my app:&lt;/p&gt;

&lt;h3 id=&quot;1-scrolling-placement&quot;&gt;1. Scrolling Placement&lt;/h3&gt;

&lt;p&gt;My app presents a map with weather data and various charts. With mobile devices having smaller screens, scrolling is a necessity. This means that the map widget must scroll along with other content.&lt;/p&gt;

&lt;p&gt;Flutter positions native platform widgets (&lt;a href=&quot;https://docs.flutter.dev/development/platform-integration/platform-views&quot;&gt;platform views&lt;/a&gt;) over top of the Flutter view hierarchy. This clever approach to layering make them appear the same as other Flutter widgets. When platform widgets are not included in a scroll view, it works beautifully. When scrolling, the platform map widget is occasionally misplaced. For example, when scrolling vertically, the map widget is placed too high, too low, or gets stuck at the bottom of the screen.&lt;/p&gt;

&lt;h3 id=&quot;2-native-crash&quot;&gt;2. Native Crash&lt;/h3&gt;

&lt;p&gt;To support creating awesome weather visualizations that can be shared on social media like &lt;a href=&quot;https://www.instagram.com/p/CUl5ZnaFvpr/&quot;&gt;this one&lt;/a&gt;, the app has to be able to render maps to an image. To do that, a &lt;a href=&quot;https://pub.dev/documentation/google_maps_flutter/latest/google_maps_flutter/GoogleMapController/takeSnapshot.html&quot;&gt;snapshot&lt;/a&gt; of the map is taken and composed with other components. A &lt;a href=&quot;https://github.com/flutter/flutter/issues/81690&quot;&gt;bug in the Google Maps package&lt;/a&gt; occasionally results in a native crash when taking a snapshot. When such a crash occurs, the app is terminated - a terrible user experience.&lt;/p&gt;

&lt;h3 id=&quot;tackling-the-issue-failed-attempt&quot;&gt;Tackling the Issue: Failed Attempt&lt;/h3&gt;

&lt;p&gt;My first attempt to fix the issues was to create my own platform widget package to embed a platform-native Google Map. I knew that I could solve the native crash problem, and was hopeful that I could address widget positioning. After a few hours, I had something up and running, but was unable to achieve a user experience that was good enough. It was clear that any approach using platform-native widgets just wouldn’t work due to the widget positioning issue.&lt;/p&gt;

&lt;h3 id=&quot;flutter-native-maps&quot;&gt;Flutter Native Maps&lt;/h3&gt;

&lt;p&gt;It turns out that there is a whole ecosystem of mapping alternatives to Google Maps, mostly focused on the web and built in JavaScript. One such library, &lt;a href=&quot;https://leafletjs.com/&quot;&gt;Leaflet&lt;/a&gt;, has inspired an excellent Flutter map implementation named Fleaflet, also known as &lt;a href=&quot;https://github.com/fleaflet/flutter_map&quot;&gt;flutter_map&lt;/a&gt;. By embedding a map using Flutter components only, i.e. without platform views, I could avoid all of the rough edges that come with native platform components in the Flutter widget hierarchy.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flutter_map&lt;/code&gt; worked beautifully, but had rough edges of its own because map tiles were pre-rendered as raster images and scaled on the client. As a result, when zooming map tiles become slightly blurry, text sizes change, and theming is limited to themes supported by the service offering raster tiles.&lt;/p&gt;

&lt;h3 id=&quot;flutter-maps-and-vector-tiles&quot;&gt;Flutter Maps and Vector Tiles&lt;/h3&gt;

&lt;p&gt;Unlike a raster tile, which is an image, vector tiles instead represent map data as a set of points, polygons, lines and metadata with relative positioning. Clients render these vectors, resulting in sharp maps at any scale.&lt;/p&gt;

&lt;p&gt;Another advantage of vector tiles is that map data can be rendered with a variety of themes, meaning that the color of geographic features such as land, roads and water can be any color as defined by the theme. This is especially relevant for mobile devices supporting dark mode.&lt;/p&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flutter_map&lt;/code&gt; to produce the beautiful maps that we have come to expect, vector tile support is essential.&lt;/p&gt;

&lt;h3 id=&quot;second-attempt-support-vector-tiles-in-flutter&quot;&gt;Second Attempt: Support Vector Tiles in Flutter&lt;/h3&gt;

&lt;p&gt;As an experiment I decided to try drawing vector tiles using Flutter and Dart. How hard could it be? It turns out that drawing vector tiles is achievable. Within a few days I had rudimentary support for themes, basic land and water shapes, lines and text as a dart library &lt;a href=&quot;https://github.com/greensopinion/dart-vector-tile-renderer&quot;&gt;vector_tile_renderer&lt;/a&gt;&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;I’m learning how to render vector tiles for maps in Dart &lt;a href=&quot;https://t.co/YRmvpyQwJp&quot;&gt;https://t.co/YRmvpyQwJp&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/dartlang?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#dartlang&lt;/a&gt; &lt;a href=&quot;https://twitter.com/hashtag/maps?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#maps&lt;/a&gt; &lt;a href=&quot;https://t.co/wxXc3sJGKu&quot;&gt;pic.twitter.com/wxXc3sJGKu&lt;/a&gt;&lt;/p&gt;&amp;mdash; David Green (@dgreen) &lt;a href=&quot;https://twitter.com/dgreen/status/1398666608231424006?ref_src=twsrc%5Etfw&quot;&gt;May 29, 2021&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/greensopinion/dart-vector-tile-renderer/commits/main&quot;&gt;commit history&lt;/a&gt; tells the story of starting with the simplest possible thing that could work, and evolving the design from there. You can see how the details of the original tile shown in the above tweet evolved too:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/rendered-tile.png&quot; class=&quot;img-responsive center border&quot; /&gt;&lt;/p&gt;

&lt;p&gt;223 commits later, I have &lt;a href=&quot;https://github.com/greensopinion/flutter-vector-map-tiles&quot;&gt;added support&lt;/a&gt; for vector map tiles to &lt;a href=&quot;https://pub.dev/packages/flutter_map&quot;&gt;flutter_map&lt;/a&gt; with great results:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2021-12/embedded-flutter-map.png&quot; class=&quot;img-responsive center border&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;whats-next&quot;&gt;What’s Next&lt;/h3&gt;

&lt;p&gt;Vector tiles support in Flutter is good, but still needs work.&lt;/p&gt;

&lt;p&gt;A major challenge is performance: Flutter needs to render each frame in less than 16ms in order to achieve 60 frames per second. Flutter uses the &lt;a href=&quot;https://skia.org&quot;&gt;Skia&lt;/a&gt; framework to render Canvas rendering operations efficiently. Despite using Skia, which renders directly to &lt;a href=&quot;https://developer.apple.com/metal/&quot;&gt;Metal&lt;/a&gt; on iOS, map vector tile rendering can be slow enough to drop frames during animations.&lt;/p&gt;

&lt;p&gt;Another area for improvement is more complete support for &lt;a href=&quot;https://docs.mapbox.com/mapbox-gl-js/style-spec/&quot;&gt;themes and theme styles&lt;/a&gt;. Currently, some theme style syntax is unsupported so it’s not possible to pick some existing themes and have them work as-is.&lt;/p&gt;

&lt;p&gt;While there is more that can be done to further improve vector tile support in Flutter maps, it’s currently good enough that I’ve been running it in production now for several months. If you want to try it out, feel free to run the &lt;a href=&quot;https://github.com/greensopinion/flutter-vector-map-tiles&quot;&gt;example app&lt;/a&gt; or check out my app &lt;a href=&quot;https://www.epicrideweather.com&quot;&gt;Epic Ride Weather&lt;/a&gt; which is available on Google Play and the App Store.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;maps attribution: Copyright &lt;a href=&quot;https://stadiamaps.com/&quot;&gt;Stadia Maps&lt;/a&gt;, &lt;a href=&quot;https://openmaptiles.org&quot;&gt;OpenMapTiles&lt;/a&gt;, &lt;a href=&quot;http://openstreetmap.org&quot;&gt;OpenStreetMap&lt;/a&gt; contributors&lt;/em&gt;&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2021/12/28/flutter-vector-maps.html"&gt;Flutter Maps With Vector Tiles&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Tue, 28 Dec 2021 12:00:00 -0800</pubDate>
        <link>https://www.greensopinion.com/2021/12/28/flutter-vector-maps.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2021/12/28/flutter-vector-maps.html</guid>
        
        <category>Vector Maps</category>
        
        
      </item>
    
      <item>
        <title>Raspberry Pi SSH Setup</title>
        <description>&lt;p&gt;I previously blogged about the &lt;a href=&quot;/2020/04/26/raspberry-pi-development-flow.html&quot;&gt;Raspberry Pi Development Flow&lt;/a&gt;. A key component enabling that development flow is SSH, a tool that will enable you to copy files and run remote commands on the Raspberry Pi.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2020-04/raspberry-pi-ssh-login.png&quot; alt=&quot;SSH to Raspberry Pi&quot; /&gt;
&lt;em&gt;Remote login to a Raspberry Pi&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Following are the steps that you can take to configure SSH on your new Raspberry Pi.&lt;/p&gt;

&lt;p&gt;If you haven’t yet got a Raspberry Pi, you’ll need to get one. I purchased mine from &lt;a href=&quot;https://www.canakit.com&quot;&gt;CanaKit&lt;/a&gt;. I got the &lt;a href=&quot;https://www.canakit.com/raspberry-pi-4-starter-kit.html&quot;&gt;starter kit&lt;/a&gt;, which arrived about 3 days after I placed the order.&lt;/p&gt;

&lt;p&gt;Once you’ve got it powered on with a keyboard, mouse and monitor attached, you can follow these steps to setup SSH in a way that supports minimal fuss:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#before-you-get-started&quot;&gt;Before You Get Started&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#enable-ssh-on-the-pi&quot;&gt;Enable SSH on the Pi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#create-a-new-user-on-the-pi&quot;&gt;Create a New User on the Pi&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#setup-for-login-without-a-password&quot;&gt;Setup for Login Without a Password&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#give-your-pi-a-name&quot;&gt;Give Your Pi a Name&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;background&quot;&gt;Background&lt;/h3&gt;

&lt;p&gt;SSH (&lt;a href=&quot;https://en.wikipedia.org/wiki/Secure_Shell&quot;&gt;Secure Shell&lt;/a&gt;) is a tool that enables you to copy files and run remote commands on your Raspberry Pi. SSH uses cryptography to ensure that your remote sessions are secure. SSH has a server portion (a daemon process) that runs on the Raspberry Pi waiting for commands, and a client portion (a command-line tool) that you can run from your development machine. With SSH you’ll be able to remotely control your Raspberry Pi, and even work on the Raspberry Pi as if you were right there in a terminal.&lt;/p&gt;

&lt;h3 id=&quot;before-you-get-started&quot;&gt;Before You Get Started&lt;/h3&gt;

&lt;p&gt;Note the IP address of your Raspberry Pi. To do that, follow these steps on the Raspberry Pi:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open a terminal&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifconfig&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Look for the section named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlan0&lt;/code&gt; (if on Wi-Fi) or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; (if on a wired network)&lt;/li&gt;
  &lt;li&gt;Locate the line that says &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inet&lt;/code&gt; in that section, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inet 192.168.1.250&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Write down the 4-part IP address&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ifconfig
eth0: &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;4099&amp;lt;UP,BROADCAST,MULTICAST&amp;gt;  mtu 1500
        ether dc:a6:32:62:6a:63  txqueuelen 1000  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Ethernet&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        RX packets 0  bytes 0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.0 B&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.0 B&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10&amp;lt;host&amp;gt;
        loop  txqueuelen 1000  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Local Loopback&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        RX packets 0  bytes 0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.0 B&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.0 B&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu 1500
        inet 192.168.1.250  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 2001:569:7eb7:9400:3037:daab:bcf3:2fdc  prefixlen 64  scopeid 0x0&amp;lt;global&amp;gt;
        inet6 fe80::bec9:e792:710f:84f0  prefixlen 64  scopeid 0x20&amp;lt;&lt;span class=&quot;nb&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
        ether dc:a6:32:62:6a:64  txqueuelen 1000  &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Ethernet&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        RX packets 150  bytes 27774 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;27.1 KiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 86  bytes 14509 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;14.1 KiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above example, the IP address from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlan0&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.250&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;enable-ssh-on-the-pi&quot;&gt;Enable SSH on the Pi&lt;/h3&gt;

&lt;p&gt;To start with, you’ll need to enable the SSH daemon process on the Raspberry Pi. To do that, from the keyboard plugged into the Raspberry Pi follow these steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open a terminal&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo raspi-config&lt;/code&gt;. More details on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspi-config&lt;/code&gt; can be found here: &lt;a href=&quot;https://www.raspberrypi.org/documentation/configuration/raspi-config.md&quot;&gt;raspberrypi.org raspi-config&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Using the arrow keys (up/down/left/right) navigate to &lt;strong&gt;Interface Options -&amp;gt; SSH&lt;/strong&gt; and select &lt;strong&gt;Yes&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;(optional) If you don’t want the Raspberry Pi to be running a graphical interface, it can be set to boot to a terminal instead. To do that, select &lt;strong&gt;Boot Options -&amp;gt; Desktop/CLI -&amp;gt; Console&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Finish&lt;/strong&gt; to exit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspi-config&lt;/code&gt; tool&lt;/li&gt;
  &lt;li&gt;If prompted, select &lt;strong&gt;Yes&lt;/strong&gt; to reboot the Raspberry Pi&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;log-in-to-the-pi&quot;&gt;Log In to the Pi&lt;/h3&gt;

&lt;p&gt;From now on, we’ll be running commands from your development machine (i.e. not the Raspberry Pi)&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Login to your Raspberry Pi from your development machine using the following command: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh pi@192.168.1.250&lt;/code&gt; (where the IP address that you noted earlier is substituted)&lt;/li&gt;
  &lt;li&gt;You may be prompted as follows:
    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The authenticity of host &lt;span class=&quot;s1&quot;&gt;'i (192.168.1.250)'&lt;/span&gt; can&lt;span class=&quot;s1&quot;&gt;'t be established.
ECDSA key fingerprint is SHA256:22yuFgmgNxTvEnpnEogya+hpNGECgyExp3hsx5vgAi8.
Are you sure you want to continue connecting (yes/no)?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yes&lt;/code&gt; then press enter&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;When prompted, enter your password for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pi&lt;/code&gt; user on the Raspberry Pi&lt;/li&gt;
  &lt;li&gt;If successful, you should see a command prompt that looks something like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pi@raspberrypi:~ $&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;create-a-new-user-on-the-pi&quot;&gt;Create a New User on the Pi&lt;/h3&gt;

&lt;p&gt;This step is optional.
To avoid having to specify a username every time we login to the Raspberry Pi, we’ll create a new user on the Raspberry Pi with the same username that we have on our development machine as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Identify your username on your development machine by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami&lt;/code&gt; from a terminal. My username &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dgreen&lt;/code&gt;, which I’ll use in examples from here forward. Substitute your own in the following steps.&lt;/li&gt;
  &lt;li&gt;ssh into the Raspberry Pi (per the previous step)&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo useradd -m dgreen&lt;/code&gt; to create the new user&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo passwd dgreen&lt;/code&gt; to give the new user account a password&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups&lt;/code&gt; to find out which groups you should add to the new user account
    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pi@raspberrypi:~ &lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;groups
&lt;/span&gt;pi adm dialout cdrom &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;audio video plugdev games &lt;span class=&quot;nb&quot;&gt;users &lt;/span&gt;input netdev gpio i2c spi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo usermod -a -G pi,adm,dialout,cdrom,sudo,audio,video,plugdev,games,users,input,netdev,gpio,i2c,spi dgreen&lt;/code&gt; to add the new user to the relevant groups&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To test out the new user, ssh back into the pi as follows from your development machine:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh 192.168.1.250&lt;/code&gt; to ssh into the Raspberry Pi. Notice that we’re not specifying a username.&lt;/li&gt;
  &lt;li&gt;Enter a password when prompted&lt;/li&gt;
  &lt;li&gt;Do your happy dance, then run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;setup-for-login-without-a-password&quot;&gt;Setup for Login Without a Password&lt;/h3&gt;

&lt;p&gt;To setup login without a password, we’re going to generate a public/private key pair on the development machine. If you’ve done this before, you can skip this step.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh-keygen -t rsa&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Accept the default path for the file&lt;/li&gt;
  &lt;li&gt;When prompted, provide a password that will be used to encrypt the private key
It should look something like this:
    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ssh-keygen &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; rsa
 Generating public/private rsa key pair.
 Enter file &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;which to save the key &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;/home/dgreen/.ssh/id_rsa&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:
 Created directory &lt;span class=&quot;s1&quot;&gt;'/home/dgreen/.ssh'&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
 Enter passphrase &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;empty &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;no passphrase&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;:
 Enter same passphrase again:
 Your identification has been saved &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /home/dgreen/.ssh/id_rsa.
 Your public key has been saved &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /home/dgreen/.ssh/id_rsa.pub.
 The key fingerprint is:
 SHA256:R8BPBghF7BT6NwTLMDV6arrPw0RhPXYfeXl4usJS7QY dgreen@ahost
 The key&lt;span class=&quot;s1&quot;&gt;'s randomart image is:
 +---[RSA 2048]----+
 |    +BB+o. . o   |
 |    o*B+o.= + o  |
 |   .o=+o.=.+ +   |
 |    .+.. .E o    |
 |   .o . So.o .   |
 |   o.  ..oo +    |
 |  .o     . o     |
 |   oo            |
 |  ..o.           |
 +----[SHA256]-----+
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Copy your public key to the Raspberry Pi as follows: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh-copy-id 192.168.1.250&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh-add&lt;/code&gt; to add your private key to the SSH agent running on your development machine. You’ll need to do this once before using SSH to log in to your Raspberry Pi every time you restart your development machine.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;give-your-pi-a-name&quot;&gt;Give Your Pi a Name&lt;/h3&gt;

&lt;p&gt;Finally, give your Raspberry Pi a name so that you don’t have to memorize it’s IP address. To do that:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;On your development machine, edit your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/hosts&lt;/code&gt; file and add a line that looks like this:
    &lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;192.168.1.250 pi
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;Try it out, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh pi&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re feeling really adventurous, you can configure your router with an IP reservation for the mac address on your Raspberry Pi. I’ll leave this one up to you to figure out.&lt;/p&gt;

&lt;h3 id=&quot;tips-and-wrap-up&quot;&gt;Tips and Wrap Up&lt;/h3&gt;

&lt;p&gt;That’s it! From now on, your development flow can occur over SSH. You can run remote commands, copy files, restart processes, and control your Raspberry Pi in other ways from any computer on the same network as the Raspberry Pi.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Copy files using SCP, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp myproject.tgz pi:/home/dgreen&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run remote commands using SSH, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh pi &quot;pm2 stop myproject &amp;amp;&amp;amp; rm -rf myproject &amp;amp;&amp;amp; tar -xzf myproject.tgz &amp;amp;&amp;amp; pm2 start myproject&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Login using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh pi&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the &lt;a href=&quot;/2020/04/26/raspberry-pi-development-flow.html&quot;&gt;optimal development flow&lt;/a&gt;, the next step is to automate the deployment of code and configuration to your Raspberry Pi.&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2020/04/26/raspberry-pi-ssh-setup.html"&gt;Raspberry Pi SSH Setup&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Sun, 26 Apr 2020 13:00:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2020/04/26/raspberry-pi-ssh-setup.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2020/04/26/raspberry-pi-ssh-setup.html</guid>
        
        <category>Raspberry Pi</category>
        
        <category>SSH</category>
        
        
      </item>
    
      <item>
        <title>Raspberry Pi Development Flow</title>
        <description>&lt;p&gt;As part of my grand plan to introduce my kids to programming and having fun while self-isolating, I decided to get into the world of Raspberry PI projects.
While reading many of the fantastic project how-tos and tutorials that are available, I didn’t see much out there related to an optimal development flow.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2020-04/raspberry-pi-development-flow.png&quot; alt=&quot;Raspberry Pi Development Flow&quot; /&gt;
&lt;em&gt;Development flow for the Raspberry Pi&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What is an optimal development flow? To me, it is the setup that enables you to rapidly experiment with changes.&lt;/p&gt;

&lt;p&gt;The key here is “rapid”. An optimal development flow includes a very fast feedback loop, so that you know whether your changes are producing the desired result. If you can make 20 changes in an hour, your rate of learning will be much, much faster (for example) than 2 or 3 changes in an hour.&lt;/p&gt;

&lt;p&gt;To enable a rate of change that fast, you’ll need to automate as much as possible to reduce the friction of deploying and trying out your changes. That way, not only will you be able to deploy and test more quickly, but the cognitive load will be greatly reduced so that your brain can stay focused on moving your project forward.&lt;/p&gt;

&lt;p&gt;Here are the main elements that make up a great development flow:&lt;/p&gt;

&lt;h3 id=&quot;coding&quot;&gt;Coding&lt;/h3&gt;

&lt;p&gt;Write code on your main development machine, i.e. not the Raspberry Pi. Yes, the Raspberry Pi 4 can run like a desktop - but it’s nowhere near as fast as a good development machine. Develop on the machine that runs fast, has a familiar keyboard and mouse/trackpad, and all of your usual development tools.&lt;/p&gt;

&lt;p&gt;Having a develoment setup separately from the Raspberry Pi also makes it easy to finetune your development tools for multiple Raspberry Pi projects.&lt;/p&gt;

&lt;h3 id=&quot;code-editor-or-ide&quot;&gt;Code Editor or IDE&lt;/h3&gt;

&lt;p&gt;Choose a code editor or IDE that is optimal for the kind of development that you’re doing. At a minimum, it should have syntax highlighting for your language of choice, enable writing/running tests, and integrate with your source control. For me, this is usually &lt;a href=&quot;https://code.visualstudio.com&quot;&gt;Visual Studio Code&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;source-control&quot;&gt;Source Control&lt;/h3&gt;

&lt;p&gt;If you haven’t already, it’s worth learning how to use Git and GitHub. You’ll never have to worry about code backups again, and you’ll have a timeline of changes to your code that will make it easy to track down when a bug was introduced, or roll back to a point in time if needed.&lt;/p&gt;

&lt;h3 id=&quot;deployment&quot;&gt;Deployment&lt;/h3&gt;

&lt;p&gt;If you’re not writing your code on the Raspberry Pi (don’t) then you’ll need a way to get your changes there. The best way is to use SSH to copy your files there. SSH will even enable you to run commands remotely, so that you can build, run and restart your project.&lt;/p&gt;

&lt;h3 id=&quot;automation&quot;&gt;Automation&lt;/h3&gt;

&lt;p&gt;Whether you’re developing on a Mac, Linux or Windows machine, make sure that you have a way to automate your deployments. For me that means having a reasonable terminal (bash or zsh) that will enable you to script your deployments. Ideally you’ll have a single script that will build, deploy and restart your project on your Raspberry PI from your development machine.&lt;/p&gt;

&lt;h3 id=&quot;a-typical-cycle-in-my-development-flow&quot;&gt;A Typical Cycle In My Development Flow&lt;/h3&gt;

&lt;p&gt;A typical cycle looks something like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Make changes to the code in &lt;a href=&quot;https://code.visualstudio.com&quot;&gt;VS Code&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Write some tests to make sure that it works&lt;/li&gt;
  &lt;li&gt;Deploy to the Raspberry Pi: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$ ./deploy.sh&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Verify that the the changes produced the desired result on the Raspberry Pi&lt;/li&gt;
  &lt;li&gt;Commit and push to GitHub &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit -m 'some message' &amp;amp;&amp;amp; git push&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Rinse and repeat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that there is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;no manual backup of the code&lt;/li&gt;
  &lt;li&gt;no worrying about backing up files or settings, or anything on the Raspberry Pi&lt;/li&gt;
  &lt;li&gt;no messing around copying files&lt;/li&gt;
  &lt;li&gt;no multi-step processes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;To automate anything on your Raspberry Pi, you’ll want to have SSH setup. See &lt;a href=&quot;/2020/04/26/raspberry-pi-ssh-setup.html&quot;&gt;Raspberry Pi SSH Setup&lt;/a&gt; for detailed steps describing how to do that.&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2020/04/26/raspberry-pi-development-flow.html"&gt;Raspberry Pi Development Flow&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Sun, 26 Apr 2020 11:30:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2020/04/26/raspberry-pi-development-flow.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2020/04/26/raspberry-pi-development-flow.html</guid>
        
        <category>Raspberry Pi</category>
        
        <category>SSH</category>
        
        
      </item>
    
      <item>
        <title>Troubleshooting Android App Crash on Chromebook</title>
        <description>&lt;p&gt;One thing is true for all developers - that is, with wide enough distribution, if a user can do something with your app, they will.
If you’re building apps for Android, you will eventually have users running your app on their Chromebook. This is great for those
looking for a laptop-like experience with your Android app, but what do you do when your app crashes only on Chromebook?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2020-02/chrome-android.png&quot; class=&quot;pull-left img-responsive mw128&quot; /&gt; The &lt;a href=&quot;https://developer.android.com/topic/arc&quot;&gt;official ARC developer docs&lt;/a&gt; have some great materials on troubleshooting, involving putting the device into “Developer mode” and connecting adb. While that may be great for your development environment, it’s clearly not practical for end users. As it turns out, there’s a much easier way. Here’s what I did:&lt;/p&gt;

&lt;p&gt;Have the user reproduce the issue. When the app crashes, they should select the “Send feedback” option. The feedback mechanism may not complete, but that’s okay since it will save feedback to the Chromebook logs.&lt;/p&gt;

&lt;p&gt;Next, have the user put the following in the address bar of Chrome &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chrome://net-internals&lt;/code&gt;
and then select “ChromeOS”.&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;Chrome net-internals&quot; src=&quot;/images/blog/2020-02/chrome-net-internals.png&quot; class=&quot;border center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;They can then click the “Store System and User Logs” button. After a few moments, a tarball containing logs will show up in their Downloads folder, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;combined-logs_20200222-081143.tar.gz&lt;/code&gt;
Have them send that to you. In my case, the file was too big for email so I used a file sharing service (e.g. via Google Drive, Dropbox, etc.)&lt;/p&gt;

&lt;p&gt;When you receive the tarball, extract it and open the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;feedback/arc-bugreport&lt;/code&gt;. If you’re lucky, you’ll find a stack trace of your app crash in there. Mine looked like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;02-22 08:04:13.732  1608  1608 I art     : Rejecting re-init on previously-failed class java.lang.Class&amp;lt;com.greensopinion.rideweather.activity.StatusActivity&amp;gt;: java.lang.LinkageError: Method boolean com.greensopinion.rideweather.activity.StatusActivity.isResumed() overrides final method in class Landroid/app/Activity; (declaration of 'com.greensopinion.rideweather.activity.StatusActivity' appears in /data/app/com.greensopinion.rideweather-1/base.apk:classes2.dex)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Class java.lang.VMClassLoader.findLoadedClass!(java.lang.ClassLoader, java.lang.String) (VMClassLoader.java:-2)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Class java.lang.ClassLoader.findLoadedClass(java.lang.String) (ClassLoader.java:742)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:362)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
02-22 08:04:13.732  1608  1608 I art     :   at android.app.Activity android.app.Instrumentation.newActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent) (Instrumentation.java:1078)
02-22 08:04:13.732  1608  1608 I art     :   at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2626)
02-22 08:04:13.732  1608  1608 I art     :   at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2800)
02-22 08:04:13.732  1608  1608 I art     :   at void android.app.ActivityThread.-wrap12(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
02-22 08:04:13.732  1608  1608 I art     :   at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1527)
02-22 08:04:13.732  1608  1608 I art     :   at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:102)
02-22 08:04:13.732  1608  1608 I art     :   at void android.os.Looper.loop() (Looper.java:154)
02-22 08:04:13.732  1608  1608 I art     :   at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6320)
02-22 08:04:13.732  1608  1608 I art     :   at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
02-22 08:04:13.732  1608  1608 I art     :   at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:891)
02-22 08:04:13.732  1608  1608 I art     :   at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:781)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You might wonder why I’d override a final method in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;android.app.Activity&lt;/code&gt;. Well, the short answer is that I made a mistake. For me the method only showed up on the Chromebook - i.e. not in the Android SDK, in the emulator or on my two Android test devices. It turns out that I should have known, see &lt;a href=&quot;https://developer.android.com/reference/android/app/Fragment#isResumed()&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fragment.isResumed()&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the stack trace in-hand, it’s a trivial matter to resolve the issue. Best of all, no “Developer mode” required on the Chromebook!&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2020/02/21/troubleshoot-android-app-crash-on-chromebook.html"&gt;Troubleshooting Android App Crash on Chromebook&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Fri, 21 Feb 2020 09:30:00 -0800</pubDate>
        <link>https://www.greensopinion.com/2020/02/21/troubleshoot-android-app-crash-on-chromebook.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2020/02/21/troubleshoot-android-app-crash-on-chromebook.html</guid>
        
        <category>Chromebook</category>
        
        <category>Android</category>
        
        <category>Crash</category>
        
        <category>Stack trace</category>
        
        
      </item>
    
      <item>
        <title>Your Team Might Not Be As Effective As You Think</title>
        <description>&lt;p&gt;I’ve had the privilege of working with highly effective teams for most of my career. In particular, the past 5 years have been incredible, working with the best software developers that I’ve ever known. The kind of teams that books are written about — embracing software craftsmanship, self-organization, agility (small ‘a’), responsibility and continuous improvement. The kind of teams that are principled, that share knowledge freely and deliver high quality software in a repeatable, sustainable fashion. So you can imagine that it came as a shock to me, when arriving at a new company, that the new team felt better in ways that I didn’t fully understand.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2019-01/effective-diversity.png&quot; alt=&quot;Effective Diversity&quot; /&gt;&lt;/p&gt;

&lt;p&gt;My realization wasn’t instantaneous. It was a gradual, multi-day process. In hindsight, it should have been evident right away, but it was my own learning process that slowed it down. My experience was simple: in a series of meetings, discussions, and collaboration opportunities, I was working with teams of people who were not all like me. Not all white, not all male, not all with a tech background, not all the same.&lt;/p&gt;

&lt;p&gt;Vancouver is somewhat different than many places in the world in that we have a melting pot of many different cultures and ethnic backgrounds — so ethnic and cultural diversity wasn’t new to me — but one thing really stood out at Ayogo that changed the dynamic that I was used to. In almost every situation, I was working with teams that had not just one or two women, but many women. In some cases, they comprised more than half of the team.&lt;/p&gt;

&lt;p&gt;My realization is that these teams take a different approach to problem solving. The way that ideas, concerns and questions are put forward is different. The way that individuals are given space to add their unique value and perspective is different. I find myself wanting to sit back, waiting a few seconds here and there in the discussion to see who else will offer an opinion, leaving space for others to move in. I continue to be impressed with the unexpected directions that decisions take, going down paths that would not have occurred to me on my own.&lt;/p&gt;

&lt;p&gt;I’ve come to realize that while individual contribution is critically important, perhaps even more important is the composition of the team. Having teams with varied backgrounds, skills, knowledge and perspective opens up all kinds of possibilities. Uniform teams are blind to these possibilities, not because they aren’t good enough, but because the nature of their composition makes them incapable of seeing. They don’t know what they don’t know.&lt;/p&gt;

&lt;p&gt;I reached out to the team with my observations and asked for their opinions. Ayogis jumped in with enthusiasm! We now have a conversation going — around the table, on Slack, and now here. Mine is just one perspective, but my point of view has now been expanded by opening it up to everyone.&lt;/p&gt;

&lt;p&gt;I’m having a blast at Ayogo — discovering what’s possible with teams that are unlike any other. Highly effective teams, the best that I’ve ever known, because of their diversity.&lt;/p&gt;

          
        </description>
        <pubDate>Tue, 22 Jan 2019 12:30:00 -0800</pubDate>
        <link>https://www.greensopinion.com/2019/01/22/your-team-might-not-be-as-effective-as-you-think.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2019/01/22/your-team-might-not-be-as-effective-as-you-think.html</guid>
        
        <category>Diversity</category>
        
        
      </item>
    
      <item>
        <title>Faster Feedback Loop on Code Reviews</title>
        <description>&lt;p&gt;We’ve all seen those projects that start with fast builds. Fast builds that build everything and run all of the tests are a great way to get feedback on your code review, whether it be &lt;a href=&quot;https://docs.travis-ci.com/user/pull-requests/&quot;&gt;Travis commenting on your GitHub Pull Request&lt;/a&gt; or &lt;a href=&quot;https://wiki.jenkins.io/display/JENKINS/Gerrit+Trigger&quot;&gt;Jenkins voting on your Gerrit code review&lt;/a&gt;. It’s almost inevitable that successful projects end up with growing build times, often to the point where the feedback loop on our changes is out of control.  This post is about solving that problem.&lt;/p&gt;

&lt;h3 id=&quot;traditional-approach&quot;&gt;Traditional Approach&lt;/h3&gt;

&lt;p&gt;I’ve seen builds go from a few minutes, to 8 minutes, to 12, 15, and before you know it builds are taking more than 40 minutes.  Along the way we convince ourselves that it’s OK, until eventually build times are out of control and review feedback cycle times are out the window.  Keeping build times to under 20 minutes is key to any kind of efficiency, with sub-10 minute builds being the sweet spot.&lt;/p&gt;

&lt;p&gt;Fixing the problem becomes a monumental task as the complexity of the project grows, and prioritizing that work always seems to take a back seat to more urgent feature delivery.  To make it worse, as the team grows and one team becomes two or more, the responsibility of fixing the issue is not squarely within a single team and so the problem continues to go unsolved.&lt;/p&gt;

&lt;p&gt;Our initial cut at reducing build times took some of the usual tacks:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;parallel builds with better modularity&lt;/li&gt;
  &lt;li&gt;faster build machines&lt;/li&gt;
  &lt;li&gt;running fewer concurrent builds so that build machines have a lighter load&lt;/li&gt;
  &lt;li&gt;profiling/optimizing long-running integration tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These all made a difference, but we eventually reached the limit of what we could achieve with these approaches and still, build times were reaching at best 35 minutes and in excess of 50 minutes on a bad day.&lt;/p&gt;

&lt;h3 id=&quot;a-new-take&quot;&gt;A New Take&lt;/h3&gt;

&lt;p&gt;If we can’t make our tests any faster, and we can’t make the build machines run those tests any faster, what if we just ran fewer tests?  Common sense tells us that we need to run &lt;em&gt;all&lt;/em&gt; the tests - but what if we don’t need to?&lt;/p&gt;

&lt;p&gt;With the premise that everything on master has &lt;em&gt;already passed all of the tests&lt;/em&gt;, then it follows that we really only need to run tests for the code that’s affected by the last change, that is, the change in our code review.&lt;/p&gt;

&lt;p&gt;By taking the intersection of the last commit (i.e. the code review) with the transitive Maven POM dependencies and hierarchy, we can dynamically determine which projects should run tests.  This is relatively trivial by using the Maven Plugin APIs and JGit (for Git repositories).&lt;/p&gt;

&lt;p&gt;To run this experiment, I created the &lt;a href=&quot;https://github.com/greensopinion/maven-change-impact&quot;&gt;maven-change-impact&lt;/a&gt; Maven plug-in.  Details are on &lt;a href=&quot;https://github.com/greensopinion/maven-change-impact&quot;&gt;the project page on GitHub&lt;/a&gt;, but to summarize: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven-change-impact&lt;/code&gt; plug-in sets a Maven property based on  analysis of the Git change and the POMs.  This property can then be used to enable or skip execution of tests using the &lt;a href=&quot;http://maven.apache.org/surefire/maven-surefire-plugin/&quot;&gt;Maven Surefire Plugin&lt;/a&gt; on a per-project basis.&lt;/p&gt;

&lt;h3 id=&quot;results&quot;&gt;Results&lt;/h3&gt;

&lt;p&gt;We’ve been running with this experiment now for two weeks and we’ve seen impressive results.&lt;/p&gt;

&lt;p&gt;On average, our builds have come down to 12 minutes.  Some builds are running as fast as 4 or 5 minutes, while others are in the 18-20 minute range.
This is a massive improvement over our previous average which was in the range of 40-50 minutes.&lt;/p&gt;

&lt;h3 id=&quot;side-effects&quot;&gt;Side-Effects&lt;/h3&gt;

&lt;p&gt;With faster builds, we have the initial benefit of faster feedback cycle time which translates to keeping developers in the flow.&lt;/p&gt;

&lt;p&gt;But, there are other benefits too:&lt;/p&gt;

&lt;p&gt;With faster builds, we’ve seen an increase in the number of review verification builds that we’re running.  In other words, we have better throughput.  This translates to being able to move more changes through our value stream, faster.  In other words, more value to the customer and improved ability to react to change.&lt;/p&gt;

&lt;p&gt;Developers are no longer incentivized to create large reviews.  More but smaller changes reduces the cognitive overhead in code reviews, and makes for better separation of unrelated changes.  So developers are able to collaborate more effectively, and it’s easier to understand the scope of any given change.&lt;/p&gt;

&lt;h3 id=&quot;applicability&quot;&gt;Applicability&lt;/h3&gt;

&lt;p&gt;This approach will benefit teams the most in cases where build times are long primarily due to test execution times, and you have reasonably good modularity in your codebase.&lt;/p&gt;

&lt;p&gt;For example, if you want to run integration tests in addition to unit tests to verify code reviews and your integration tests take more than a few seconds each, test execution times can really add up.  This definitely applies in our case, where we have more than 30,000 tests.&lt;/p&gt;

&lt;h3 id=&quot;try-it-out&quot;&gt;Try It Out&lt;/h3&gt;

&lt;p&gt;I invite you to try out the &lt;a href=&quot;https://github.com/greensopinion/maven-change-impact&quot;&gt;maven-change-impact&lt;/a&gt; to see if it makes a difference for you and your team.  Enjoy, and don’t hesitate to let me know if it works out for you!&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2018/06/13/faster-feedback-loop-on-code-reviews.html"&gt;Faster Feedback Loop on Code Reviews&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Wed, 13 Jun 2018 13:30:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2018/06/13/faster-feedback-loop-on-code-reviews.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2018/06/13/faster-feedback-loop-on-code-reviews.html</guid>
        
        <category>Code Review</category>
        
        <category>Build Time</category>
        
        
      </item>
    
      <item>
        <title>Surfacing Abstractions in Code Reviews</title>
        <description>&lt;p&gt;Code review tools such as &lt;a href=&quot;https://github.com/features/code-review&quot;&gt;GitHub PRs&lt;/a&gt; and &lt;a href=&quot;https://www.gerritcodereview.com&quot;&gt;Gerrit&lt;/a&gt; do a a great job of enabling developers to collaborate, but their file- and line-oriented approach leaves abstractions out of the picture.  Developers must have the capacity to see the abstractions by reading the code, or risk having code reviews devolve into a critique of syntax, whitespace and implementation detail. The intrinsic ability to see abstractions comes easily for some, but others are at a disadvantage especially when less familiar with the codebase.  To address this issue, I conducted an experiment in surfacing code abstractions as a diagram in code reviews.&lt;/p&gt;

&lt;p&gt;My premise was that a static class diagram would enable developers to see the major abstractions and their relationships independently of the implementation detail.  By having this higher level view of the code related to a change, developers would have a deeper understanding when performing a code review.  My goal was two-fold: to enable ramp-up of less experienced engineers, and to draw more attention to the abstractions since in many cases they as if not more important than the implementation.  (A poor abstraction implemented perfectly is in many cases less valuable than a great abstraction with a sub-optimal implementation.)&lt;/p&gt;

&lt;p&gt;To run this experiment, I created &lt;a href=&quot;https://github.com/greensopinion/gerrit-class-diagram-plugin&quot;&gt;a plug-in for Gerrit&lt;/a&gt; that dynamically generates UML static class diagrams for any code review.  These diagrams are intentionally missing detail, taking the &lt;a href=&quot;https://martinfowler.com/bliki/UmlAsSketch.html&quot;&gt;UML-as-sketch&lt;/a&gt; approach to avoid overly-complicated diagrams.&lt;/p&gt;

&lt;p&gt;I was looking for answers to the following questions in this experiment:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Could diagrams be automatically generated that were good enough to communicate the major abstractions?&lt;/li&gt;
  &lt;li&gt;Is it possible to pull in more of the surrounding context so that the changed code can be evaluated within the context of a larger codebase?&lt;/li&gt;
  &lt;li&gt;Would developers find these diagrams useful?&lt;/li&gt;
  &lt;li&gt;Would these diagrams affect the code review process sufficiently to driver higher quality changes to non-trivial systems?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result of this experiment was surprising.  A simple, well-structured code review produced the following diagram:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2018-04/simple-generated-diagram-detail.png&quot; alt=&quot;Example Generated Diagram&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is what it looks like on the Gerrit code review:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2018-04/simple-generated-diagram.png&quot; alt=&quot;Example Generated Diagram&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So far, so good.  But, what about a more complicated example?  Following is a diagram generated from a real contribution:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2018-04/complex-generated-diagram.png&quot; alt=&quot;Another Example Generated Diagram&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This diagram is much harder to read, even when zoomed in.  My initial reaction was that this diagram was not useful at all. The code and its surrounding context were simply too complicated to enable generation of a useful diagram.  After saying that to myself, it dawned on me: This is exactly why the diagram is useful.  The generated diagram is exposing code for what it is: overly complicated with poor abstractions and too many dependencies.&lt;/p&gt;

&lt;p&gt;In my experience, most developers aren’t as practiced at reading static class diagrams as they are at reading code. For these diagrams to be useful, developers would need to become proficient at reading them.  Proficiency comes with practice, but for that to happen, developers would have to be willing to try out the approach.&lt;/p&gt;

&lt;p&gt;This experiment isn’t over.  The next step is to try it out for a few weeks and get feedback from developers.&lt;br /&gt;
What do you think, would this be helpful for you or your team?  If you’re interested to try it out, the project has been &lt;a href=&quot;https://github.com/greensopinion/gerrit-class-diagram-plugin&quot;&gt;published on GitHub&lt;/a&gt;.&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2018/04/27/surfacing-abstractions-in-code-reviews.html"&gt;Surfacing Abstractions in Code Reviews&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Fri, 27 Apr 2018 14:30:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2018/04/27/surfacing-abstractions-in-code-reviews.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2018/04/27/surfacing-abstractions-in-code-reviews.html</guid>
        
        <category>Architecture</category>
        
        
      </item>
    
      <item>
        <title>Architecting for the Value Stream</title>
        <description>&lt;p&gt;More features, faster time to market, fewer defects.  These are what organizations strive for. But an overly narrow continuous integration and continuous delivery focus on DevOps is insufficient to achieve these goals. Instead, we should be taking a whole-system point of view that considers factors beyond development and operations — including people, teams, processes and tools.  By taking a constraints perspective, we can identify and address flow-limiting aspects of our value stream.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2018-01/software-architecture.jpeg&quot; class=&quot;pull-right img-responsive&quot; /&gt; 
The foundation of “good” architecture — principles such as modularity, separation of concerns and SOLID — deliver the promise of flexibility, maintainability and all of the other “ilities” that we value.  But these qualities, while enabling faster and sustainable delivery of business value, fail on their own to address some constraints that limit the value stream.  For example, delivery aspects such as multiple teams; new team member ramp-up; support and troubleshooting; and user feedback impose constraints on our value stream.  By taking a wider perspective than only technically driven architecture aspects, we can optimize it to remove some constraints.&lt;/p&gt;

&lt;h3 id=&quot;collaboration-with-multiple-delivery-teams&quot;&gt;Collaboration with multiple delivery teams&lt;/h3&gt;

&lt;p&gt;Multiple delivery teams can deliver more features faster than one, but this only holds true if introducing more teams doesn’t introduce additional constraints.  But multiple teams introduce greater potential for conflicts and corresponding churn as more people work on the same codebase, build pipeline, tests and infrastructure.  So how do we minimize this problem with our architecture?&lt;/p&gt;

&lt;p&gt;With a technology-first approach, you may architect your module or microservice boundaries in a way that optimizes separation of concerns and cohesion, e.g. loosely coupled functionality grouped by related concepts. Meanwhile, architecting value stream first considers the alignment of teams and value streams to the technology. As such, you may create API, microservice and module boundaries that provide autonomy to teams, enabling them to deliver features independently of other teams. And you will plan for evolving those APIs to avoid wait states that can lead to slower cycle times.&lt;/p&gt;

&lt;p&gt;For example, if a team must wait four weeks for a new API that is required for a new feature, you would refine your architectural process to remove the dependency, provide an accelerated way of having that team contribute to the API, or take the architectural debt of copying the functionality while ensuring you have a path to remove that debt.&lt;/p&gt;

&lt;p&gt;Let’s take this thinking and apply it to other aspects of our delivery: build and test infrastructure.  Is one team causing build breakages for another?  While modifying a test fixture configuration, does that team cause another’s tests to fail?  Our natural tendencies might be to share build and test environments to avoid duplication and minimize redundancy.  If you are architecting these systems value stream-first, you will design the build and test pipeline and corresponding environments in a way that insulates each team. Meanwhile, a share-nothing approach, while it may introduce redundancy, could be a great trade-off to eliminate dependencies and friction between teams.&lt;/p&gt;

&lt;p&gt;One way of achieving share-nothing builds is to have tests provision their own environments for the required duration.  For example, a test suite that verifies services running over MySQL could bring up a containerized MySQL instance for the duration of the test, and destroy it when the test is complete.  Effort is required to enable tests to accomplish this level of provisioning; however once complete the burden of creating, maintaining and administering long-running test fixtures is eliminated.&lt;/p&gt;

&lt;h3 id=&quot;feedback-loop&quot;&gt;Feedback loop&lt;/h3&gt;

&lt;p&gt;A good feedback loop is an essential part of enabling the value stream.  Tests provide feedback to engineers so that they know whether the system works.  Fast tests, running earlier in the process (for example in the IDE) create a shorter feedback loop, enabling engineers to react more easily and more quickly.  A great feedback loop, however, goes beyond test speed and the trade-off between unit tests and integration tests per the Test Pyramid.  Test speed is important, but only covers a very small part of the engineering delivery cycle.&lt;/p&gt;

&lt;p&gt;The biggest constraint on receiving user feedback is typically the user. To remove this constraint, you must remove the user, which is a paradox since user feedback is what we seek.  While it may not be possible to remove the user for all user feedback, we can for a class of it.  By architecting a product that automatically provides feedback without the user having to instigate it, we can approximate user feedback and eliminate the constraint.&lt;/p&gt;

&lt;p&gt;For example, you should build metrics into the application so that you know before the user if a new feature is too slow.  Monitoring, instrumentation, telemetry and analytics are essential feedback tools that should be integrated into every application, not just mobile apps.&lt;/p&gt;

&lt;h3 id=&quot;cycle-time-and-sustainability&quot;&gt;Cycle time and sustainability&lt;/h3&gt;

&lt;p&gt;In many organizations, focusing on the value stream means delivering new user features. Often, it’s seen as a business decision as to whether to deliver a new feature and possibly incur additional technical debt or pay off existing technical debt. It is indeed a business decision, but making any decision should include understanding the consequences of that choice.&lt;/p&gt;

&lt;p&gt;The true cost of technical debt is often misunderstood. By definition it slows down you, your team or the organization. While one small technical debt may not impact you significantly, pile up a few of them and your velocity will suffer. Pile up more than a few of them, and beyond slower delivery speeds, team morale and team culture will suffer.&lt;/p&gt;

&lt;p&gt;The choice of whether to accumulate or to address new technical debt is often made without understanding the scope and quantity of existing technical debt. Take great care to avoid a pile-up. Instead err toward having little or no technical debt at all. While it may sound like an unattainable goal, it is worth pursuing, since it enables teams to perform at their highest level.&lt;/p&gt;

&lt;h3 id=&quot;new-team-member-ramp-up&quot;&gt;New team member ramp-up&lt;/h3&gt;

&lt;p&gt;Staff turnover is a normal part of software delivery. Consider the time required to make new staff productive, the mistakes they make while ramping up and their consequences. These strategies can help on-boarding issues:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Avoid documenting, instead automate: Automated things don’t require learning to get them right, and if done well are self-documenting. Automated things run the same way every time, don’t require learning how and tend to run very fast. Automating everything is a great strategy for reducing the on-boarding burden.&lt;/li&gt;
  &lt;li&gt;Modular systems are easier to learn than monolithic systems because of their decoupled nature. Components have fewer dependencies, and are grouped by functionality. This means that new team members have less to learn in specific areas, since they don’t need to understand the whole system. This translates to faster time-to-value.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thinking holistically about the value stream as it relates to the whole organization can provide insights into improved software architectures. A traditional architecture view is limited to the technical merit of a solution; however you may need a broader view to address the competitive needs of modern organizations by delivering a steady stream of value to customers.  Seek architectural strategies to address issues that involve more than just the technical aspects of software delivery. Consider too, the teams and people that are involved from idea to delivery and operations.&lt;/p&gt;

          
        </description>
        <pubDate>Mon, 22 Jan 2018 12:00:00 -0800</pubDate>
        <link>https://www.greensopinion.com/2018/01/22/architecting-for-the-value-stream.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2018/01/22/architecting-for-the-value-stream.html</guid>
        
        <category>Architecture</category>
        
        
      </item>
    
      <item>
        <title>ELK Stack for Improved Support</title>
        <description>&lt;p&gt;The ELK stack, composed of &lt;a href=&quot;https://www.elastic.co/products/elasticsearch&quot;&gt;Elasticsearch&lt;/a&gt;, &lt;a href=&quot;https://www.elastic.co/products/logstash&quot;&gt;Logstash&lt;/a&gt; and &lt;a href=&quot;https://www.elastic.co/products/kibana&quot;&gt;Kibana&lt;/a&gt;, is world-class dashboarding for real-time monitoring of server environments, enabling sophisticated analysis and troubleshooting.  Could we also leverage this great tooling to our advantage in situations where access to the server environment is an impossibility?  Recently while investigating a customer support case, I looked into whether or not we could create a repeatable process to enable analysis of log files provided as part of a support case.  Following are details of the approach that we came up with.&lt;/p&gt;

&lt;p&gt;The requirements were pretty straight-forward:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Enable analysis of multi-GB logs provided as part of a support request&lt;/li&gt;
  &lt;li&gt;Use familiar, first-class tooling&lt;/li&gt;
  &lt;li&gt;Zero-installation usage by anyone on the support or engineering team&lt;/li&gt;
  &lt;li&gt;Zero maintenance (no infrastructure needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this we chose &lt;a href=&quot;https://www.elastic.co/products&quot;&gt;ELK&lt;/a&gt; and &lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;Docker Compose&lt;/a&gt;, with the idea that anyone could bring up and tear down an environment with very little effort.  Rather than monitor logs in real time however, we needed to pull in logs from a folder on the local machine.  For this we used &lt;a href=&quot;https://www.elastic.co/products/beats/filebeat&quot;&gt;Filebeat&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; that we came up with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;elk:
  image: sebp/elk
  ports:
    - &quot;5601:5601&quot;
    - &quot;9200:9200&quot;
    - &quot;5044:5044&quot;
  volumes:
    - ${PWD}/02-beats-input.conf:/etc/logstash/conf.d/02-beats-input.conf
    - ${PWD}/log:/mnt/log
filebeat:
  image: docker.elastic.co/beats/filebeat:5.5.1
  links:
    - &quot;elk:logstash&quot;
  volumes:
    - ${PWD}/filebeat.yml:/usr/share/filebeat/filebeat.yml
    - ${PWD}/log:/mnt/log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This Docker Compose file brings up two containers: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;elk&lt;/code&gt;, which as you might have guessed runs Elasticsearch, Logstash and Kibana, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filebeat&lt;/code&gt;, a container for reading log files that feeds the elk container with data.&lt;/p&gt;

&lt;p&gt;The filebbeat container is the most interesting one: it reads files from a local folder named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log&lt;/code&gt; in the current directory of the Docker &lt;em&gt;host&lt;/em&gt; machine.  With the brilliance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${PWD}&lt;/code&gt; support in Docker Compose, all we have to do is move support log files into that folder!&lt;/p&gt;

&lt;p&gt;The following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filebeat.yml&lt;/code&gt; configuration is needed:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;filebeat.prospectors:
- input_type: log
  paths:
    - /mnt/log/*
  include_lines: [&quot;.*? ERROR &quot;]
  multiline.pattern: ^\s*\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d\d\d \[
  multiline.negate: true
  multiline.match: after

processors:
- add_cloud_metadata:

output.logstash:
  # The Logstash hosts
  hosts: [&quot;logstash:5044&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This one is configured to handle multi-line log entries (including Java stack traces) where the initial line of each log entry starts with a timestmap.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multiline.pattern&lt;/code&gt; above may need adjusting to suit your log files.&lt;/p&gt;

&lt;p&gt;All that remains to get this working is the beats configuration, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;02-beats-input.conf&lt;/code&gt;, which uses a bit of filtering hackery to split up the unstructured log entries into structured data before it’s added to Elasticsearch:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;input {
  beats {
    port =&amp;gt; 5044
  }
}
filter {
  grok {
    match =&amp;gt; {
      &quot;message&quot; =&amp;gt; &quot;\s*(?&amp;lt;entry_date&amp;gt;\d\d\d\d-\d\d-\d\d) (?&amp;lt;entry_time&amp;gt;\d\d:\d\d:\d\d),(?&amp;lt;entry_time_millis&amp;gt;\d\d\d) \[(?&amp;lt;thread_id&amp;gt;[^\]]+)\] (?&amp;lt;severity&amp;gt;[^\s]+) (?&amp;lt;category&amp;gt;[^\s]+) - (?:(?&amp;lt;error_code&amp;gt;CCRRTT-\d+(E|W)):\s+)?(?&amp;lt;message_text&amp;gt;.*)&quot;
    }
  }
  mutate {
    add_field =&amp;gt; {
      &quot;entry_timestamp&quot; =&amp;gt; &quot;%{entry_date}T%{entry_time}.%{entry_time_millis}Z&quot;
    }
    remove_field =&amp;gt; [&quot;entry_date&quot;, &quot;entry_time&quot;, &quot;entry_time_millis&quot;]
  }
  mutate {
    remove_field =&amp;gt; [&quot;message&quot;]
  }
  mutate {
    add_field =&amp;gt; {
      &quot;message&quot; =&amp;gt; &quot;%{message_text}&quot;
    }
    remove_field =&amp;gt; [&quot;message_text&quot;]
  }
  grok {
    match =&amp;gt; {
      &quot;message&quot; =&amp;gt; &quot;\s*(?&amp;lt;message_summary&amp;gt;.*?) Cause Context:.*&quot;
    }
  }
  grok {
    match =&amp;gt; {
      &quot;message_summary&quot; =&amp;gt; &quot;\s*(?&amp;lt;message_first_sentence&amp;gt;.*?\.).*&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After creating those files I ended up with the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./
./docker-compose.yml
./logs/
./02-beats-input.conf
./filebeat.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt;, I moved over 56GB of log files into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logs&lt;/code&gt; folder and grabbed coffee.  After a few minutes I was happily analyzing the situation using a Kibana dashboard:&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;Kibbana Dashboard&quot; src=&quot;/images/blog/2017-09/kibana-dashboard.png&quot; class=&quot;border center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this example, we can see a chart of error codes and distinct messages over time.&lt;/p&gt;

&lt;p&gt;To make this process even smoother, we used &lt;a href=&quot;https://www.npmjs.com/package/elasticdump&quot;&gt;elasticdump&lt;/a&gt; to export our Kibana dashboards for other support cases.&lt;/p&gt;

&lt;p&gt;To export dashboards:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;elasticdump --input=http://localhost:9200/.kibana --output=$ --type=data &amp;gt; kibana-settings.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To import dashboards:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;elasticdump --input=./kibana-settings.json --output=http://localhost:9200/.kibana --type=data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using ELK for post-mortem analysis of log files is a snap.  The approach outlined above makes the process repeatable with trivial steps that anyone can follow, with no need to maintain ELK infrastructure.&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2017/09/09/elk-stack-for-improved-support.html"&gt;ELK Stack for Improved Support&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Sat, 09 Sep 2017 16:11:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2017/09/09/elk-stack-for-improved-support.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2017/09/09/elk-stack-for-improved-support.html</guid>
        
        <category>ELK</category>
        
        <category>Elasticsearch</category>
        
        <category>Logstash</category>
        
        <category>Kibana</category>
        
        
      </item>
    
      <item>
        <title>Using Eclipse for Android Development in 2017</title>
        <description>&lt;p&gt;Innovation in platforms and tooling is necessary for companies to remain competitive, and Android development is no exception.  The Android team at Google has been working hard in this department, with their latest major new thing being &lt;a href=&quot;https://android-developers.googleblog.com/2017/03/future-of-java-8-language-feature.html&quot;&gt;improved Java 8 support&lt;/a&gt;.  This involves some big changes to the &lt;a href=&quot;http://tools.android.com/tech-docs/new-build-system&quot;&gt;Android build system&lt;/a&gt;, which unfortunately has caused some casualties in tooling such as &lt;a href=&quot;/2016/05/15/eclipse-for-android-development.html&quot;&gt;this previous approach to using Eclipse for Android development&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the prospect of cleaning up my Android codebase with Java 8 lambdas, I was unable to resist the new Java 8 tooling.  A suggestion from &lt;a href=&quot;http://johannesbrodwall.com/&quot;&gt;Johannes Brodwall&lt;/a&gt; got me thinking about a Gradle plug-in to simplify generating an Eclipse &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.classpath&lt;/code&gt; in a way that is compatible with the innovation going on in Android tooling.  Feeling inspired, I created &lt;a href=&quot;https://github.com/greensopinion/gradle-android-eclipse&quot;&gt;this plugin&lt;/a&gt; which does the heavy lifting, adding the necessary classpath entries and expanding Android Archive (AAR) files so that their nested jars can be included.&lt;/p&gt;

&lt;p&gt;To use the plugin, add the following to your build.gradle:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apply plugin: 'com.greensopinion.gradle-android-eclipse'
apply plugin: 'eclipse'

buildscript {
    repositories {
        maven {
          url &quot;https://plugins.gradle.org/m2/&quot;
        }
    }
    dependencies {
      classpath &quot;gradle.plugin.com.greensopinion.gradle-android-eclipse:android-eclipse:1.0&quot;
    }
}

eclipse {
  classpath {
    plusConfigurations += [ configurations.compile, configurations.testCompile ]
    downloadSources = true
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then from the command-line run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gradle eclipse
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When done, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.classpath&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.project&lt;/code&gt; file should be in the current folder.  You’ll need to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gradle eclipse&lt;/code&gt; whenever changes are made to your classpath.&lt;/p&gt;

&lt;p&gt;To find out more about the plugin, head on over to &lt;a href=&quot;https://github.com/greensopinion/gradle-android-eclipse&quot;&gt;https://github.com/greensopinion/gradle-android-eclipse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2017/04/23/eclipse-for-android-development.html"&gt;Using Eclipse for Android Development in 2017&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Sun, 23 Apr 2017 13:05:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2017/04/23/eclipse-for-android-development.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2017/04/23/eclipse-for-android-development.html</guid>
        
        <category>WikiText</category>
        
        
      </item>
    
      <item>
        <title>This In-App Purchase Has Aready Been Bought</title>
        <description>&lt;p&gt;If you’re developing iOS apps with in-app purchases, chances are you’ve seen this dreaded dialog and wondered why &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKPaymentQueue&lt;/code&gt; isn’t calling your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKPaymentTransactionObserver&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;In-App Purchase Dialog: This In-App Purchase has already been bought&quot; src=&quot;/images/blog/2017-03/ios_inapp_already_bought.png&quot; class=&quot;border center&quot; /&gt;&lt;/p&gt;

&lt;p&gt;“This In-App Purchase has already been bought.  It will be restored for free.”&lt;/p&gt;

&lt;p&gt;This scenario can easily occur when an error occurs before calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKPaymentQueue.default().finishTransaction(transaction)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Apple has designed the payment queue in such a way as to enable apps to fulfill a purchase before completing - with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finishTransaction&lt;/code&gt; being the indication that fulfillment is complete.  In the case that a failure occurs during fulfillment, the app can recover when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKPaymentQueue&lt;/code&gt; calls the app’s observer again.&lt;/p&gt;

&lt;p&gt;This is all good and fine - except &lt;a href=&quot;https://forum.unity3d.com/threads/closed-restorepurchase-on-ios-not-return-processpurchase-callback.392000/&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;http://stackoverflow.com/questions/37941466/catch-the-in-app-purchase-has-already-been-bought-event&quot;&gt;developers&lt;/a&gt; &lt;a href=&quot;http://stackoverflow.com/questions/34001868/ios-this-in-app-purchase-has-already-been-bought-pop-up&quot;&gt;have&lt;/a&gt; encountered a situation where this process breaks down.  Their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKPaymentTransactionObserver&lt;/code&gt; is never called again, and the user is presented with the dreaded “already bought” dialog and no apparent way to fix the problem.&lt;/p&gt;

&lt;p&gt;Like many problems in software development, it turns out that the problem is quite simple and completely non-obvious.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKPaymentQueue&lt;/code&gt; does indeed call a transaction observer - just not the one in your app.  A library (in my case &lt;a href=&quot;https://firebase.google.com/docs/analytics/&quot;&gt;Firebase analytics&lt;/a&gt;) is adding its own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKPaymentTransactionObserver&lt;/code&gt; before your app adds its own observer.  As a result, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SKPaymentQueue&lt;/code&gt; is calling that observer first, and by the time your observer is added, as far as the payment queue is concerned it has already delivered the notification and there’s nothing left to do.&lt;/p&gt;

&lt;p&gt;To fix the problem, ensure that your app registers its own observer before any third party libraries get a chance to do the same:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    class AppDelegate: UIApplicationDelegate {

        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -&amp;gt; Bool {

            ...

            // ORDER DEPENDENCY: before analytics
            SKPaymentQueue.default().add(createPaymentTransactionObserver())

            // ORDER DEPENDENCY: do this after store service setup!
            FIRApp.configure()

            return true
        }
        ...
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s all!&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2017/03/22/This-In-App-Purchase-Has-Already-Been-Bought.html"&gt;This In-App Purchase Has Aready Been Bought&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Wed, 22 Mar 2017 16:14:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2017/03/22/This-In-App-Purchase-Has-Already-Been-Bought.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2017/03/22/This-In-App-Purchase-Has-Already-Been-Bought.html</guid>
        
        <category>WikiText</category>
        
        
      </item>
    
      <item>
        <title>Mylyn WikiText on Maven Central</title>
        <description>&lt;p&gt;I’m excited to announce that for the first time, &lt;a href=&quot;https://wiki.eclipse.org/Mylyn/WikiText&quot;&gt;Mylyn WikiText&lt;/a&gt; is now available on Maven Central.&lt;/p&gt;

&lt;p&gt;This release enables use of Mylyn WikiText within a normal Maven project; previously Mylyn WikiText could only readily be consumed by downloading the jar files or by resolving dependencies using Tycho.&lt;/p&gt;

&lt;p&gt;Now dependencies can be resolved at the following coordinates:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.eclipse.mylyn.docs&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;org.eclipse.mylyn.wikitext&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;3.0.2&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
    ...
&amp;lt;/dependencies&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Mylyn WikiText provides several additional components (e.g. for Markdown, AsciiDoc, etc.) which are &lt;a href=&quot;https://search.maven.org/#search%7Cga%7C1%7Cmylyn.docs&quot;&gt;listed here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To celebrate the new release I’ve created an examples project on GitHub, which shows how to use Mylyn WikiText in a few different ways:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/greensopinion/wikitext-examples&quot;&gt;https://github.com/greensopinion/wikitext-examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The examples demonstrate:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Mylyn WikiText Java APIs&lt;/li&gt;
  &lt;li&gt;Mylyn WikiText Maven mojo to generate HTML help using a pom&lt;/li&gt;
  &lt;li&gt;Mylyn WikiText Ant tasks from within Maven&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From now on Mylyn WikiText releases will provide Maven artifacts from Maven Central, and p2 artifacts for OSGi and Eclipse applications.&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2017/03/06/Mylyn-WikiText-on-Maven-Central.html"&gt;Mylyn WikiText on Maven Central&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Mon, 06 Mar 2017 12:00:00 -0800</pubDate>
        <link>https://www.greensopinion.com/2017/03/06/Mylyn-WikiText-on-Maven-Central.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2017/03/06/Mylyn-WikiText-on-Maven-Central.html</guid>
        
        <category>WikiText</category>
        
        
      </item>
    
      <item>
        <title>Building a Cross-Platform Desktop Application with AngularJS and Java</title>
        <description>&lt;p&gt;Building cross-platform desktop applications usually means compromising on user experience, ease of development or both.
Below I detail the reasoning and trade-offs behind an approach that involves state of the art technologies and development methodology with fewer compromises.&lt;/p&gt;

&lt;p&gt;Web technologies (HTML, CSS and JavaScript) are arguably the best choice for a cross-platform UI in most situations.   With numerous great choices for frameworks, skills commonly available, and unparalleled flexibility in styling, it’s hard to imagine choosing anything else as long as you don’t mind your app looking like a web application.&lt;/p&gt;

&lt;p&gt;Creating applications with the likes of &lt;a href=&quot;http://electron.atom.io/&quot;&gt;Electron&lt;/a&gt; or &lt;a href=&quot;https://github.com/MacGapProject&quot;&gt;MacGap&lt;/a&gt; is nothing new - there are several successful such applications in widespread use, such as &lt;a href=&quot;https://slack.com/&quot;&gt;Slack&lt;/a&gt;, &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VisualStudio Code&lt;/a&gt; and &lt;a href=&quot;https://desktop.wordpress.com/&quot;&gt;WordPress Desktop&lt;/a&gt;.  But building a JavaScript-only application isn’t for everyone, especially in cases where reuse of existing functionality is important.&lt;/p&gt;

&lt;p&gt;Many organizations have substantial functionality available in the form of Java libraries that they would like to reuse and the corresponding skills, infrastructure and methods to build and maintain application logic in Java.  Taking an Electron-like approach to building such applications but with an emphasis on Java for the functional portion of the application could be a good choice in these cases.&lt;/p&gt;

&lt;p&gt;Java ships with a WebKit-based WebView for JavaFX.  This made a bit of a splash, as previous attempts to embed a browser in Java apps were either using a sub-par Java-based browser, or had crazy dependencies and complex setup.&lt;/p&gt;

&lt;p&gt;Creating a JavaScript application using a JavaFX WebView &lt;a href=&quot;http://www.salidasoftware.com/using-java8-build-deploy-native-html5-applications/&quot;&gt;has been done before&lt;/a&gt;.  On the surface this approach looks great - with the benefits of a state-of-the-art browser engine, standard APIs, no extra dependencies and tools to create platform-specific installers, many of the previous problems are solved.  But the approach hasn’t seen wide adoption, in part due to these shortcomings:&lt;/p&gt;

&lt;h3 id=&quot;1-html-and-css-tooling&quot;&gt;1. HTML and CSS Tooling&lt;/h3&gt;

&lt;p&gt;Web developers rely heavily on browser-based tools to develop the look and feel of an application.  Modern web browsers provide integrated tooling that provide a way to introspect and modify CSS and HTML.  With a couple of clicks, developers can troubleshoot and try out styles and HTML, live in the application. This ability to change an application as it’s running is one of the cornerstones behind web developer productivity.&lt;/p&gt;

&lt;p&gt;A JavaFX WebView doesn’t have such tooling.  The WebView is a black-box, essentially locking developers out from their most powerful development approach.  It’s like going back to the mid ’90s.&lt;/p&gt;

&lt;p&gt;One approach to overcoming this limitation is to include &lt;a href=&quot;https://getfirebug.com/firebuglite&quot;&gt;Firebug Lite&lt;/a&gt;, which enables inspection of HMTL and CSS in almost any browser, including the JavaFX WebView.  I did get it working, and though it’s better than not having any tooling, not all versions of Firebug Lite worked for me and I was unable to edit CSS properties live in the application.  To be fair, Firebug Lite is not intended as a replacement for Firebug or Chrome development tools.  In the end I found that it was insufficient for my needs.&lt;/p&gt;

&lt;h3 id=&quot;2-javascript-debugging&quot;&gt;2. JavaScript Debugging&lt;/h3&gt;

&lt;p&gt;No matter how good your JavaScript automated testing approach, there is no substitute for debugging JavaScript live in a running application.  It’s the last-resort fall-back to figuring out what’s really happening in your application.  With no built-in JavaScript debugger, the JavaFX WebView left me feeling like it was the early days of the web.&lt;/p&gt;

&lt;h3 id=&quot;3-javascript-to-java-and-back-again&quot;&gt;3. JavaScript to Java and Back Again&lt;/h3&gt;

&lt;p&gt;With the UI in a WebView, calling out to your Java application (or the reverse, calling into the JavaScript web UI) is an important aspect of this approach.  Unfortunately crossing the JavaScript-Java boundary is not entirely straight-forward.  Though it’s possible to invoke JavaScript in the WebView from Java, and call back to Java from JavaScript, it’s not transparent.&lt;/p&gt;

&lt;p&gt;Data type conversion is very limited; most non-trivial use cases will require Java code that introspects &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSObject&lt;/code&gt;, a generic JavaScript type wrapper.  This makes for clumsy Java code and in most cases reduces the Java-to-JavaScript boundary to a message-passing paradigm.&lt;/p&gt;

&lt;h3 id=&quot;4-webkit-version&quot;&gt;4. WebKit Version&lt;/h3&gt;

&lt;p&gt;Having a WebKit-based browser ship with the Java platform is great, but which version is it?  It appears that updating WebKit on the Java platform is &lt;a href=&quot;https://bugs.openjdk.java.net/secure/attachment/49253/webkit-merge-journal.txt&quot;&gt;quite a bit of work&lt;/a&gt;, since Oracle is not updating the WebKit version very frequently.  As of this writing, Java 8 ships with WebKit version 537.44, which is from 2013.&lt;/p&gt;

&lt;p&gt;If the embedded WebKit is used as a web browser - that is, to browse the web, not just for your application - then having an up-to-date WebKit is critical for security.&lt;/p&gt;

&lt;h3 id=&quot;5-download-size&quot;&gt;5. Download Size&lt;/h3&gt;

&lt;p&gt;Distributing a JRE with the application is needed to ensure that the application runs consistently and to keep installation simple (avoiding the need to install a JRE separately).  As a result the application size is over 100MB, which could be problematic for some situations.&lt;/p&gt;

&lt;h2 id=&quot;mitigating-the-drawbacks&quot;&gt;Mitigating the Drawbacks&lt;/h2&gt;

&lt;p&gt;With a laundry list of drawbacks our approach of using a JavaFX WebView to host the UI for a desktop Java application is sounding pretty bad so far.  But, with a concrete mitigation strategy for each issue, it could be worth exploring further:&lt;/p&gt;

&lt;h3 id=&quot;web-development-approach&quot;&gt;Web Development Approach&lt;/h3&gt;

&lt;p&gt;Web development tooling leverages our team’s expertise and is key to productivity - so how can we come up with a development approach that enables this?&lt;/p&gt;

&lt;p&gt;It turns out that we can build a traditional web application by implementing REST services as the interface between our UI and our application.  By having the UI call out to REST services, we can have our cake and eat it too.&lt;/p&gt;

&lt;p&gt;With this approach our application architecture looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2016-06/greenbeans-application-architecture.png&quot; alt=&quot;JavaFX Application Architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In development we can deploy the application as a normal web application like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/blog/2016-06/greenbeans-dev-architecture.png&quot; alt=&quot;Web Application Architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;By providing an in-memory HTTP/REST bridge to these services we can run the UI and application logic in one process, while in development we can split the application from the UI by using a web browser and web container.  This means that we get the single-process deployment profile of a desktop application, while retaining the web development tools and techniques that we value so much.&lt;/p&gt;

&lt;h3 id=&quot;java-to-javascript-impedance-mismatch&quot;&gt;Java to JavaScript Impedance Mismatch&lt;/h3&gt;

&lt;p&gt;To overcome the difficulty of type compatibility and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSObject&lt;/code&gt; called out above, we turn to our REST services again.  JavaScript with frameworks like AngularJS is purpose-built to call REST services.  And, by passing JSON data over HTTP (or over the REST bridge) all of our data is serialized to a standard format (JSON) which is trivial to deserialize on the Java end.  In fact, the impedance mismatch completely disappears by implementing REST services and it feels like developing a “normal” web application.&lt;/p&gt;

&lt;h2 id=&quot;build-system&quot;&gt;Build System&lt;/h2&gt;

&lt;p&gt;A major problem for any project is the build system.  Maven, Ant, Gradle - whatever you choose, we still need something to build the webby front-end.  For this application I chose Grunt with Node, Karma, Jasmine… it’s all there.  Ultimately this is the same as any web application using more than one implementation language, there are two build technologies involved, which complicates things significantly.&lt;/p&gt;

&lt;h2 id=&quot;testing-strengths&quot;&gt;Testing Strengths&lt;/h2&gt;

&lt;p&gt;Web technologies for testing the UI are fairly mature and enable testing on several levels:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Unit testing is pretty straight-forward when using AngularJS, Karma and Jasmine, enabling a high level test coverage for JavaScript backing the UI with a reasonable effort.&lt;/li&gt;
  &lt;li&gt;Karma and Jasmine can also be used to test for elements in the DOM.&lt;/li&gt;
  &lt;li&gt;Though I haven’t done it here, with use of &lt;a href=&quot;http://www.seleniumhq.org/projects/webdriver/&quot;&gt;Selenium WebDriver&lt;/a&gt; it’s possible to test application functionality via the UI.&lt;/li&gt;
  &lt;li&gt;Using a Java back-end, it’s really simple to use normal unit testing techniques and frameworks, for example with JUnit and Mockito.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Selecting web technologies for the UI makes thorough testing a possibility, covering all aspects of our application architecture.&lt;/p&gt;

&lt;p&gt;The only real problem here is that our UI tests will be running using a web browser rather than the JavaFX WebView.  It’s hard to say if that’s a major gap or if it’s something that we could live with - I think only experience would shed light in that area.&lt;/p&gt;

&lt;h2 id=&quot;migration-to-web&quot;&gt;Migration to Web&lt;/h2&gt;

&lt;p&gt;If future application deployment as a real web application rather than a desktop application is a consideration, then this approach could be a great way to move forward.  The application architecture being a traditional web stack lends itself very well to a web deployment.&lt;/p&gt;

&lt;h2 id=&quot;working-example&quot;&gt;Working Example&lt;/h2&gt;

&lt;p&gt;A working example of such an application is &lt;a href=&quot;https://github.com/greensopinion/greenbeans&quot;&gt;published on GitHub&lt;/a&gt;.  I’d love to hear your feedback and thoughts on the application and the approach.&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;Building a desktop application using a JavaFX WebView with an embedded AngularJS app is not simple to set up, but does enable reuse of existing Java services and business logic.  For teams that are want to leverage existing skills (e.g. Java and HTML/JavaScript/CSS) this could be a good choice, as long as having a desktop application look like a web application is OK.&lt;/p&gt;

&lt;p&gt;Many, but not all of the development difficulties introduced by running a web application in a JavaFX WebView instead of a standard web browser can be mitigated by supporting a normal web development flow as outlined above.&lt;/p&gt;

&lt;p&gt;For me this was &lt;a href=&quot;/2016/03/30/learning-new-things.html&quot;&gt;a great learning experience&lt;/a&gt; and potentially worthy of further exploration.  Would I use this as a product development approach for a real application?  Maybe, if the trade-offs were to weigh in favour for the needs of the organization and application under consideration.  But I’m not completely sold on it - so would keep my eye open for signs of an unhealthy development process and architecture.&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2016/06/01/cross-platform-desktop-application-with-angularjs.html"&gt;Building a Cross-Platform Desktop Application with AngularJS and Java&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Wed, 01 Jun 2016 13:00:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2016/06/01/cross-platform-desktop-application-with-angularjs.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2016/06/01/cross-platform-desktop-application-with-angularjs.html</guid>
        
        <category>AngularJS</category>
        
        <category>Java</category>
        
        <category>WebView</category>
        
        <category>JavaFX</category>
        
        
      </item>
    
      <item>
        <title>Firebase Mobile Analytics First Experiences</title>
        <description>&lt;p&gt;More information is better when it comes to improving any application.  With a mission to find out how people are using a new Android app that I’m working on, I set out to integrate analytics into my mobile app.  Imagine my excitement to discover that &lt;a href=&quot;https://searchenginewatch.com/2016/05/19/google-launches-firebase-analytics-for-mobile-apps/&quot;&gt;Google has launched Firebase Analytics&lt;/a&gt; for mobile apps at Google I/O 2016. It took me all of about 20 minutes to get the initial analytics integration working.  Here’s what I did:&lt;/p&gt;

&lt;h4 id=&quot;1-setup-a-firebase-account&quot;&gt;1. Setup a Firebase Account&lt;/h4&gt;

&lt;p&gt;The first step is to &lt;a href=&quot;https://firebase.google.com/&quot;&gt;create a Firebase account&lt;/a&gt;.  This was really simple using my Google credentials.  Firebase was able to create a new analytics project for my existing mobile application project.  To do that I selected “Import Google Project” and provided the package name (found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AndroidManifest.xml&lt;/code&gt;), and the SHA1 certificate fingerprint from my signing digital cert.  The fingerprint can be obtained with a command like this one:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; $ keytool -list -v -keystore ~/my-keystore.jks
 ...snip..

 Alias name: alias_name
 Creation date: 25-May-2016
 Entry type: PrivateKeyEntry
 Certificate chain length: 1
 Certificate[1]:
 Owner: CN=David Green, OU=Unknown, O=David Green, L=Vancouver, ST=British Columbia, C=CA
 Issuer: CN=David Green, OU=Unknown, O=David Green, L=Vancouver, ST=British Columbia, C=CA
 Serial number: 60eef983
 Valid from: Wed May 25 21:02:06 PDT 2016 until: Sun Oct 11 21:02:06 PDT 2043
 Certificate fingerprints:
 	 MD5:  24:4C:92:9B:0D:01:26:E0:1A:49:DF:01:8F:3B:63:A0
 	 SHA1: 5B:16:23:8A:A0:30:8D:B6:B6:7F:7B:96:EE:F3:10:6C:95:EF:0D:9F
 	 SHA256: 2E:0E:29:D2:67:9D:12:65:46:C4:F5:46:A0:8B:D8:8F:FE:3C:A9:C5:6B:E4:A2:8E:FA:01:18:3E:6A:82:AE:37
 	 Signature algorithm name: SHA256withRSA
 	 Version: 3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;2-add-firebase-dependencies&quot;&gt;2. Add Firebase Dependencies&lt;/h4&gt;

&lt;p&gt;Next was adding Firebase dependencies to the build.  &lt;a href=&quot;https://firebase.google.com/docs/android/setup#add_the_sdk&quot;&gt;The instructions&lt;/a&gt; are great, it took all of 2 minutes.  The only hiccup is that I had to update an existing project dependency &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.google.android.gms:play-services-maps&lt;/code&gt; to the 9.0.0 version to get the build to work.&lt;/p&gt;

&lt;h4 id=&quot;3-add-analytics-to-the-app&quot;&gt;3. Add Analytics To The App&lt;/h4&gt;

&lt;p&gt;Finally adding analytics to the app was trivial.  A key step for me is that this feature has to be opt-in.  In other words, the user has to agree to providing stats, otherwise it just doesn’t feel right.  To make it easy to manage the opt-in setting I wrapped the Firebase APIs in a small class called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Analytics&lt;/code&gt; as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class Analytics {

	private static final String CONTENT_TYPE_ACTIVITY = &quot;activity&quot;;

	static final String ENABLED_KEY = &quot;enableAnalytics&quot;;

	private boolean enabled;
	private FirebaseAnalytics firebase;

	public Analytics(Context context) {
		checkNotNull(context);
		this.enabled = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ENABLED_KEY, false);
		if (enabled) {
			firebase = FirebaseAnalytics.getInstance(context);
		}
	}

	public void logActivated(Activity activity) {
		if (!enabled) {
			return;
		}
		Bundle bundle = new Bundle();
		bundle.putString(FirebaseAnalytics.Param.ITEM_ID, activity.getClass().getSimpleName());
		bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, CONTENT_TYPE_ACTIVITY);
		firebase.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make this work with all of the activities in my app, I created an Activity base class as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class AnalyticsActivity extends AppCompatActivity {
	protected Analytics analytics;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		analytics = new Analytics(this);
	}

	@Override
	protected void onResume() {
		super.onResume();
		analytics.logActivated(this);
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally with a little search-and-replace, I had all activity classes extending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AnalyticsActivity&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppCompatActivity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s it!  With just this mobile analytics will help me to understand how much and how often each activity within the app is used in much the same way as Google Analytics does the same for the web.  Now only if creating a privacy policy was as straight forward!&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2016/05/25/firebase-mobile-analytics-first-experiences.html"&gt;Firebase Mobile Analytics First Experiences&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Wed, 25 May 2016 13:00:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2016/05/25/firebase-mobile-analytics-first-experiences.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2016/05/25/firebase-mobile-analytics-first-experiences.html</guid>
        
        <category>Android</category>
        
        <category>Analytics</category>
        
        <category>Firebase</category>
        
        
      </item>
    
      <item>
        <title>Using Eclipse for Android Development in 2016</title>
        <description>&lt;p&gt;Using the best tool for the job has a direct impact to my overall sense of well-being and happiness - not to mention productivity.  For me when it comes to Java, that means using Eclipse.  Life is just too short for messing around.  Eclipse has been relegated to the back benches for Android development now that Android Studio is based on IntelliJ.  So what is an Eclipse die-hard to do?&lt;/p&gt;

&lt;p&gt;I found that learning Android Studio was pretty straight-forward.  I was up and running, creating a reasonable Android app within a couple of days, forcing myself to learn the “IntelliJ-way”, key bindings and all.  After a couple of weeks rolled by, then months, I realized that my creativity was being hampered because I just couldn’t hit the flow in IntelliJ.  The friction was affecting me: my code had fewer unit tests than I would like, and simple tasks were taking too long.  IntelliJ was just slowing me down too much and it was impacting my project.&lt;/p&gt;

&lt;p&gt;Seeing that &lt;a href=&quot;http://gradle.org/eclipse/&quot;&gt;Gradle buildship&lt;/a&gt; doesn’t yet support Android projects, A quick google for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Gradle generate Eclipse classpath&quot;&lt;/code&gt; and I &lt;a href=&quot;http://stackoverflow.com/questions/17470831/how-to-use-gradle-to-generate-eclipse-and-intellij-project-files-for-android-pro&quot;&gt;landed on this StackOverflow&lt;/a&gt;.  With a few Gradle edits to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.gradle&lt;/code&gt;, I was able to generate the Eclipse &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.classpath&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.project&lt;/code&gt; files needed to start editing Java source in Eclipse.  My configuration is based on the answer provided by &lt;a href=&quot;http://stackoverflow.com/users/27658/johannes-brodwall&quot;&gt;Johannes Brodwall&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update for 2017, the approach outlined here is now available as a Gradle plugin: see &lt;a href=&quot;/2017/04/23/eclipse-for-android-development.html&quot;&gt;Using Eclipse for Android Development in 2017&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apply plugin: 'eclipse'

eclipse {
// pathVariables 'GRADLE_HOME': gradle.gradleUserHomeDir, &quot;ANDROID_HOME&quot;: android.sdkDirectory
    classpath {
        plusConfigurations += [ configurations.compile, configurations.testCompile ]

        file {
            beforeMerged { classpath -&amp;gt;
                classpath.entries.add(new org.gradle.plugins.ide.eclipse.model.SourceFolder(&quot;src/main/java&quot;, &quot;bin&quot;))
                // Hardcoded to use debug configuration
                classpath.entries.add(new org.gradle.plugins.ide.eclipse.model.SourceFolder(&quot;build/generated/source/r/debug&quot;, &quot;bin&quot;))
                classpath.entries.add(new org.gradle.plugins.ide.eclipse.model.SourceFolder(&quot;build/generated/source/buildConfig/debug&quot;, &quot;bin&quot;))
                classpath.entries.add(new org.gradle.plugins.ide.eclipse.model.SourceFolder(&quot;build/generated/source/aidl/debug&quot;, &quot;bin&quot;))
            }

            whenMerged { classpath -&amp;gt;
                def aars = []
                classpath.entries.each { dep -&amp;gt;
                    if (dep.path.toString().endsWith(&quot;.aar&quot;)) {
                        def explodedDir = new File(projectDir, &quot;build/intermediates/exploded-aar/&quot; + dep.moduleVersion.group + &quot;/&quot; + dep.moduleVersion.name + &quot;/&quot; + dep.moduleVersion.version + &quot;/jars/&quot;)
                        if (explodedDir.exists()) {
                            explodedDir.eachFileRecurse(groovy.io.FileType.FILES) {
                                if (it.getName().endsWith(&quot;jar&quot;)) {
                                    def aarJar = new org.gradle.plugins.ide.eclipse.model.Library(fileReferenceFactory.fromFile(it))
                                    aarJar.sourcePath = dep.sourcePath
                                    aars.add(aarJar)
                                }
                            }
                        } else {
                            println &quot;Warning: Missing &quot; + explodedDir
                        }
                    }
                }
                classpath.entries.removeAll { it.path.endsWith(&quot;.aar&quot;) }
                classpath.entries.addAll(aars)

                def androidJar = new org.gradle.plugins.ide.eclipse.model.Variable(
                    fileReferenceFactory.fromPath(&quot;ANDROID_HOME/platforms/&quot; + android.compileSdkVersion + &quot;/android.jar&quot;))
                androidJar.sourcePath = fileReferenceFactory.fromPath(&quot;ANDROID_HOME/sources/&quot; + android.compileSdkVersion)
                classpath.entries.add(androidJar)
            }
        }
    }
}

eclipseClasspath.dependsOn &quot;generateDebugSources&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The main difference between my configuration and that provided on the StackOverflow is the addition of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aidl&lt;/code&gt; path.&lt;/p&gt;

&lt;p&gt;To get this going the first time, or to update it whenever the project configuration changes, simply run the command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gradle eclipse
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or, if using the AndroidStudio-generated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gradlew&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ../gradlew eclipse
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Within a couple of minutes I had the Java imports organized and compiler warnings gone, was refactoring in anger and writing unit tests with one of my favourite Eclipse plug-ins, &lt;a href=&quot;https://marketplace.eclipse.org/content/moreunit&quot;&gt;MoreUnit&lt;/a&gt;.&lt;/p&gt;

          
          &lt;p&gt;The post &lt;a href="https://www.greensopinion.com/2016/05/15/eclipse-for-android-development.html"&gt;Using Eclipse for Android Development in 2016&lt;/a&gt; originally appeared on &lt;a href="https://www.greensopinion.com"&gt;greensopinion.com&lt;/a&gt;&lt;/p&gt;
          
        </description>
        <pubDate>Sun, 15 May 2016 13:00:00 -0700</pubDate>
        <link>https://www.greensopinion.com/2016/05/15/eclipse-for-android-development.html</link>
        <guid isPermaLink="true">https://www.greensopinion.com/2016/05/15/eclipse-for-android-development.html</guid>
        
        <category>Android</category>
        
        <category>Eclipse</category>
        
        
      </item>
    
  </channel>
</rss>
