Category: Technical

Posts related to programming or other highly technical stuff that can only be of interest to computer geeks.

  • Disclaimer: I don’t know what I’m talking about, I’ve done little Win API (Win32) development and I only have a few years of Java development of which maybe 2 or 3 are developing desktop applications with JavaFX (Dashman being my first fully fledged out JavaFX app).

    Disclaimer 2: I have only tested this on my own computer, running Microsoft Windows 10. I hope to soon test it in many others and over time we’ll see whether my solution was correct or not. I’ll update this blog post accordingly (or link to a newer version if necessary).

    I started taking the quality of Dashman very seriously and one of the problems I found was that the running instances wouldn’t exit properly during uninstall or upgrades. And as I expected, this turned out into a head-bashing-into-brick-wall task. My solution was for a JavaFX app, but this should work for a Swing or any other kind of apps.

    It all started with learning about Windows Restart Manager, something I didn’t know it even existed until a week ago. This is what allows Windows to close applications on uninstall, on reboots, etc. In the Guidelines for Applications, the crucial bit is this:

    The Restart Manager queries GUI applications for shutdown by sending a WM_QUERYENDSESSION notification that has the lParam parameter set to ENDSESSION_CLOSEAPP (0x1). Applications should not shut down when they receive a WM_QUERYENDSESSION message because another application may not be ready to shut down. GUI applications should listen for the WM_QUERYENDSESSION message and return a value of TRUE if the application is prepared to shut down and restart. If no application returns a value of FALSE, the Restart Manager sends a WM_ENDSESSION message with the lParam parameter set to ENDSESSION_CLOSEAPP (0x1) and the wparam parameter set to TRUE. Applications should shut down only when they receive the WM_ENDSESSION message. The Restart Manager also sends a WM_CLOSE message for GUI applications that do not shut down on receiving WM_ENDSESSION. If any GUI application responds to a WM_QUERYENDSESSION message by returning a value of FALSE, the shutdown is canceled. However, if the shutdown is forced, the application is terminated regardless.

    Simplifying it: when Windows needs your app to close, it will send a message asking if you are ready to close. Your application might respond negatively and then no application will be closed. This could happen for example if there’s some unsaved work and the app needs the consent from the user to either save or discard. This is what happens when you try to shut down your computer and Microsoft Word stops it asking whether you want to save the file or not.

    After that your application can receive a message asking it to please close or telling it to close now. I’m not sure what the nuances are between these two. For Dashman I decided to just save the config and close in either of these instances.

    Receiving these messages requires interfacing with Windows DLLs, for which I’m using JNA. I don’t know how JNA works, I read the code, sort-of understood it, copied and pasted it. What I think is going on is that you open the user32.dll like this:

    User32 user32 = Native.loadLibrary("user32", User32.class, Collections.unmodifiableMap(options))

    User32 is an interface that contains all the methods with the proper signatures to be able to call them from Java. options just makes sure we are using the Unicode version of the Win32 API calls. You can see that and all the other missing pieces on the full example at the end of the blog post.

    I need a Win32 API callback that will receive the messages and actually implement the guidelines previously quoted:

    StdCallLibrary.StdCallCallback proc = new StdCallLibrary.StdCallCallback() {
        public WinDef.LRESULT callback(WinDef.HWND hwnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
            if (uMsg == WM_QUERYENDSESSION && lParam.intValue() == ENDSESSION_CLOSEAPP) {
                return new WinDef.LRESULT(WIN_TRUE);
            } else if ((uMsg == WM_ENDSESSION && lParam.intValue() == ENDSESSION_CLOSEAPP && wParam.intValue() == WIN_TRUE) || uMsg == WM_CLOSE) {
                Application.exit();
                return new WinDef.LRESULT(WIN_FALSE); 
            }
            return user32.DefWindowProc(hwnd, uMsg, wParam, lParam);
     
        }
    };

    Oh! Lot’s of constants! What are they? I define them in the full example at the bottom of this post. They should be mostly self-evident what they stand for, their actual values are not that important.

    Now things get tricky. Apparently Microsoft Windows send these messages to windows, not processes. Dashman can run in the tray bar, with no active window. And even if it had an active window, getting the HWND pointer for that window in JavaFX doesn’t seem trivial (I couldn’t get it to work). So, I create a size 0 invisible window to receive the message:

    WinDef.HWND window = user32.CreateWindowEx(0, "STATIC", "Dashman Win32 Restart Manager Window.", WS_MINIMIZE, 0, 0, 0, 0, null, null, null, null);

    Then I need to connect that window to the callback:

    try {
        user32.SetWindowLongPtr(window, GWL_WNDPROC, proc);
    } catch (UnsatisfiedLinkError e) {
        user32.SetWindowLong(window, GWL_WNDPROC, proc);
    }

    The callback is not magic though, and requires an event loop that will constantly check if there’s a message and trigger the processing when that happens:

    WinUser.MSG msg = new WinUser.MSG();
    while (user32.GetMessage(msg, null, 0, 0) > 0) {
        user32.TranslateMessage(msg);
        user32.DispatchMessage(msg);
    }

    Of course, that means you want this to run as its own daemon thread. The reason to make it a daemon thread is so that it won’t hang around preventing the JVM from exiting. 

    One of my most useful sources of understanding and inspiration was the source code for Briar. I want to give credit where credit is due. I do think I spotted an issue with their source code in which they are not following the guidelines though. Also, they have a much more complex situation to handle.

    And now, the full example with all my comments including links to more information explaining where all the values for constants and logic is coming from:

    import com.sun.jna.Native;
    import com.sun.jna.Pointer;
    import com.sun.jna.platform.win32.WinDef;
    import com.sun.jna.platform.win32.WinUser;
    import com.sun.jna.win32.StdCallLibrary;
    import com.sun.jna.win32.W32APIFunctionMapper;
    import com.sun.jna.win32.W32APITypeMapper;
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    import static com.sun.jna.Library.OPTION_FUNCTION_MAPPER;
    import static com.sun.jna.Library.OPTION_TYPE_MAPPER;
    
    // Inspiration can be found at https://code.briarproject.org/akwizgran/briar
    public class RestartManager {
        // https://autohotkey.com/docs/misc/SendMessageList.htm
        private static final int WM_CLOSE = 0x10; // https://msdn.microsoft.com/en-us/library/windows/desktop/ms632617
        private static final int WM_QUERYENDSESSION = 0x11; // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376890
        private static final int WM_ENDSESSION = 0x16; // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376889
    
        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376890
        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376889
        private static final int ENDSESSION_CLOSEAPP = 0x00000001;
        private static final int ENDSESSION_CRITICAL = 0x40000000;
        private static final int ENDSESSION_LOGOFF = 0x80000000;
    
        // https://stackoverflow.com/questions/50409858/how-do-i-return-a-boolean-as-a-windef-lresult
        private static final int WIN_FALSE = 0;
        private static final int WIN_TRUE = 1;
    
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
        private static final int GWL_WNDPROC = -4;
    
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
        private static final int WS_MINIMIZE = 0x20000000;
    
        public static void enable() {
            Runnable evenLoopProc = () -> {
                // Load user32.dll usi the Unicode versions of Win32 API calls
                Map<String, Object> options = new HashMap<>();
                options.put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
                options.put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
                User32 user32 = Native.loadLibrary("user32", User32.class, Collections.unmodifiableMap(options));
    
                // Function that handles the messages according to the Restart Manager Guidelines for Applications.
                // https://msdn.microsoft.com/en-us/library/windows/desktop/aa373651
                StdCallLibrary.StdCallCallback proc = new StdCallLibrary.StdCallCallback() {
                    public WinDef.LRESULT callback(WinDef.HWND hwnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
                        if (uMsg == WM_QUERYENDSESSION && lParam.intValue() == ENDSESSION_CLOSEAPP) {
                            return new WinDef.LRESULT(WIN_TRUE); // Yes, we can exit whenever you want.
                        } else if ((uMsg == WM_ENDSESSION && lParam.intValue() == ENDSESSION_CLOSEAPP
                                && wParam.intValue() == WIN_TRUE) || uMsg == WM_CLOSE) {
                            Application.exit();
                            return new WinDef.LRESULT(WIN_FALSE); // Done... don't call user32.DefWindowProc.
                        }
                        return user32.DefWindowProc(hwnd, uMsg, wParam, lParam); // Pass the message to the default window procedure
    
                    }
                };
    
                // Create a native window that will receive the messages.
                WinDef.HWND window = user32.CreateWindowEx(0, "STATIC",
                        "Dashman Win32 Restart Manager Window.", WS_MINIMIZE, 0, 0, 0,
                        0, null, null, null, null);
    
                // Register the callback
                try {
                    user32.SetWindowLongPtr(window, GWL_WNDPROC, proc); // Use SetWindowLongPtr if available (64-bit safe)
                } catch (UnsatisfiedLinkError e) {
                    user32.SetWindowLong(window, GWL_WNDPROC, proc); // Use SetWindowLong if SetWindowLongPtr isn't available
                }
    
                // The actual event loop.
                WinUser.MSG msg = new WinUser.MSG();
                while (user32.GetMessage(msg, null, 0, 0) > 0) {
                    user32.TranslateMessage(msg);
                    user32.DispatchMessage(msg);
                }
            };
    
            Thread eventLoopThread = new Thread(evenLoopProc, "Win32 Event Loop");
            eventLoopThread.setDaemon(true); // Make the thread a daemon so it doesn't prevent Dashman from exiting.
            eventLoopThread.start();
        }
    
        private interface User32 extends StdCallLibrary {
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
            WinDef.HWND CreateWindowEx(int dwExStyle, String lpClassName, String lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, WinDef.HWND hWndParent, WinDef.HMENU hMenu, WinDef.HINSTANCE hInstance, Pointer lpParam);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633572(v=vs.85).aspx
            WinDef.LRESULT DefWindowProc(WinDef.HWND hWnd, int Msg, WinDef.WPARAM wParam, WinDef.LPARAM lParam);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
            WinDef.LRESULT SetWindowLong(WinDef.HWND hWnd, int nIndex, StdCallLibrary.StdCallCallback dwNewLong);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644898(v=vs.85).aspx
            WinDef.LRESULT SetWindowLongPtr(WinDef.HWND hWnd, int nIndex, StdCallLibrary.StdCallCallback dwNewLong);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
            int GetMessage(WinUser.MSG lpMsg, WinDef.HWND hWnd, int wMsgFilterMin, int wMsgFilterMax);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644955(v=vs.85).aspx
            boolean TranslateMessage(WinUser.MSG lpMsg);
    
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644934(v=vs.85).aspx
            WinDef.LRESULT DispatchMessage(WinUser.MSG lpmsg);
        }
    }

    And now, my usual question: do you think this should be a reusable open source library? would you use it?

  • A friend of my dad introduced me to ham radio when I was 7 years old. When I was 15 or so I passed my beginner’s exam and then I did nothing with it. I got my call sign when I was 24 years old and moving out of Argentina: LU5ARC. I never used it because Argentina is not part of CEPT (and I haven’t gone back except for short holidays).

    To get that Argentinean license, I had to take three months of two evenings a week of lessons on theory, Morse code and operating a radio (just making QSOs on 80 meters). I actually collected about 10 QSLs from that time (I wish I knew where they are).

    When I moved to the UK almost 7 years ago, I looked into transferring my license but I was told it was impossible. I wish they also told me how easy it was to get a license in the UK and I wouldn’t have waited so long to get started. Last year something else got me interested in radio and I decided to take the plunge and get licensed. I was delighted to see how easy it is.

    The hardest part of getting licensed was waiting for the two day course to happen (at that point, I didn’t know about ML&S running them). Because of my previous experience with radio and the fact that I studied electronics and electromechanics in school, there was little to nothing that I didn’t know for the foundation level. Without too much effort I got my first British call sign: M6UON.

    Then, I had to wait again and I was thrilled to find that ML&S run foundation and intermediate courses, as well as advanced exams so often. I took the course, pass the exam, and I got my intermediate license: 2E0GGE. A month after that, I took the advanced exam and I now have my full license M0ONP.

    Oh… even before there was a foundation course available, I went to the RSGB convention and I took the three exams in a row for the FCC (American) license, so, even before managing to get M6UON, I got an extra (full) one for the US as AC1DM. So ironic!

    2018-04-02-15-56-49.jpg

    And now the fun begins. I lifted all possible restrictions. I can use the full 100W of my Icom IC-7300 as well as take my Icom ID-51E PLUS2 abroad and use it. I can also supervise unlicensed people so I’ve been introducing all my friends to ham radio. I either have friends that are genuinely interested in this technical hobby that’s going without them knowing about or very good friends that humor me when I spend hours explaining frequency, modulation, SWR, antennas, bandwidth, etc.

  • Update 2018-05-23: Updated the code to my current version, which fixes a few bugs.

    When doing usability testing of an alpha version of Dashman, one thing that I was strongly asked was to have the windows remember their sizes when you re-open the application. The need was clear as it was annoying to have the window be a different size when re-started.

    The new version of Dashman is built using Java and JavaFX and thus I searched for how to do this, how to restore size. I found many posts, forums, questions, etc all with the same simplistic solution: restoring width and height, and maybe position.

    What those were missing was restoring whether the window was maximized (maximized is not the same as occupying all the available space, at least in Windows). But most important than that, none of the solutions took into consideration the fact that the resolutions and quantity of screens could be different than the last time the application run, thus, you could end up with a window completely out of bounds, invisible, immobile.

    I came up with this solution, a class that’s designed to be serializable to your config to store the values but also restore them and make sure the window is visible and if not, move it to a visible place:

    // Copyright (c) 2017-2018 Flexpoint Tech Ltd. All rights reserved.
    
    package tech.dashman.dashman;
    
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import javafx.application.Platform;
    import javafx.geometry.Rectangle2D;
    import javafx.stage.Screen;
    import javafx.stage.Stage;
    import lombok.Data;
    import tech.dashman.common.Jsonable;
    
    @Data
    public class StageSizer implements Jsonable {
        private static double MINIMUM_VISIBLE_WIDTH = 100;
        private static double MINIMUM_VISIBLE_HEIGHT = 50;
        private static double MARGIN = 50;
        private static double DEFAULT_WIDTH = 800;
        private static double DEFAULT_HEIGHT = 600;
    
        private Boolean maximized = false;
        private Boolean hidden = false;
        private Double x = MARGIN;
        private Double y = MARGIN;
        private Double width = DEFAULT_WIDTH;
        private Double height = DEFAULT_HEIGHT;
    
        @JsonIgnore
        private Boolean hideable = true;
    
        @JsonIgnore
        public void setStage(Stage stage) {
            // First, restore the size and position of the stage.
            resizeAndPosition(stage, () -> {
                // If the stage is not visible in any of the current screens, relocate it to the primary screen.
                if (isWindowIsOutOfBounds(stage)) {
                    moveToPrimaryScreen(stage);
                }
                // And now watch the stage to keep the properties updated.
                watchStage(stage);
            });
        }
    
        private void resizeAndPosition(Stage stage, Runnable callback) {
            Platform.runLater(() -> {
                if (getHidden() != null && getHidden() && getHideable()) {
                    stage.hide();
                }
                if (getX() != null) {
                    stage.setX(getX());
                }
                if (getY() != null) {
                    stage.setY(getY());
                }
                if (getWidth() != null) {
                    stage.setWidth(getWidth());
                } else {
                    stage.setWidth(DEFAULT_WIDTH);
                }
                if (getHeight() != null) {
                    stage.setHeight(getHeight());
                } else {
                    stage.setHeight(DEFAULT_HEIGHT);
                }
                if (getMaximized() != null) {
                    stage.setMaximized(getMaximized());
                }
                if (getHidden() == null || !getHidden() || !getHideable()) {
                    stage.show();
                }
    
                new Thread(callback).start();
            });
        }
    
        public void setHidden(boolean value) {
            this.hidden = value;
        }
    
        private boolean isWindowIsOutOfBounds(Stage stage) {
            for (Screen screen : Screen.getScreens()) {
                Rectangle2D bounds = screen.getVisualBounds();
                if (stage.getX() + stage.getWidth() - MINIMUM_VISIBLE_WIDTH >= bounds.getMinX() &&
                    stage.getX() + MINIMUM_VISIBLE_WIDTH <= bounds.getMaxX() &&
                    bounds.getMinY() <= stage.getY() && // We want the title bar to always be visible.
                    stage.getY() + MINIMUM_VISIBLE_HEIGHT <= bounds.getMaxY()) {
                    return false;
                }
            }
            return true;
        }
    
        private void moveToPrimaryScreen(Stage stage) {
            Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
            stage.setX(bounds.getMinX() + MARGIN);
            stage.setY(bounds.getMinY() + MARGIN);
            stage.setWidth(DEFAULT_WIDTH);
            stage.setHeight(DEFAULT_HEIGHT);
        }
    
        private void watchStage(Stage stage) {
            // Get the current values.
            setX(stage.getX());
            setY(stage.getY());
            setWidth(stage.getWidth());
            setHeight(stage.getHeight());
            setMaximized(stage.isMaximized());
            setHidden(!stage.isShowing());
    
            // Watch for future changes.
            stage.xProperty().addListener((observable, old, x) -> setX((Double) x));
            stage.yProperty().addListener((observable, old, y) -> setY((Double) y));
            stage.widthProperty().addListener((observable, old, width) -> setWidth((Double) width));
            stage.heightProperty().addListener((observable, old, height) -> setHeight((Double) height));
            stage.maximizedProperty().addListener((observable, old, maximized) -> setMaximized(maximized));
            stage.showingProperty().addListener(observable -> setHidden(!stage.isShowing())); // Using an invalidation instead of a change listener due to this weird behaviour: https://stackoverflow.com/questions/50280052/property-not-calling-change-listener-unless-theres-an-invalidation-listener-as
        }
    }
    

    and the way you use it is quite simple. On your start method, you create or restore an instance of StageSizer and then do this:

    public void start(Stage stage) {
        StageSizer stageSizer = createOrRestoreStageSizerFromConfig();
        stageSizer.setStage(stage);
    }
    

    I haven’t put a lot of testing on this code yet but it seems to work. Well, at least on Windows. The problem is that this snippet is interacting with the reality of screen sizes, resolutions, adding and removing monitors, etc. If you find a bug, please, let me know and I might release this a library with the fix so we can keep on collectively improving this.

  • Next month, I’m taking my test to get a ham radio licence here in the UK. This is not my first licence. I’ve got LU5ARC years ago, as I was leaving Argentina, so, I never really got to use it. Years before that I’ve got a Yaesu FT411E, but I never transmitted with it due to lack of licence.

    I don’t want that to happen again, so, as soon as I get my licence I want to hit the ground running and start using it. My plan right now is:

    1. Get a VHF/UHF hand held radio.
    2. Learn about antennas by reading:
    3. Get an antenna for VHF/UHF on my car (probably magnetic).
    4. Attempt to install a VHF/UHF vertical antenna in my house.
    5. Decide based on the information I have so far whether I can go for HF.
    6. Set up an HF base station.

    Right now, I’m concerned about step 1. These are the things I’m after, in this order of preference:

    1. VHF
    2. UHF
    3. Ability to use a stationary vertical antenna
    4. Weather proofing
    5. D-Star (with GPS)
    6. APRS (with GPS)

    I could try to get everything now, or maybe do it in stages. I’m not sure yet. I narrowed down my selection to these 5 radios:

    • Kenwood TH-D74
    • Icom ID-51 PLUS2
    • Icom IC-E91
    • Yaesu FT-60E
    • A Baofeng

    Kenwood TH-D74

    The ideal radio seems to exist and it’s the Kenwood TH-D74 (Buy: US). From what I’m seeing this piece of equipment is insane. It does everything and it does almost everything well (except maybe battery life). The price is also insane. If money was no object I would just get this radio and I’m done. I’d probably use it for many, many years.

    Kenwood TH-D74

    Icom ID-51 PLUS2

    The runner up is the Icom ID-51 PLUS2 (Buy: US, UK). It’s more than 35% cheaper than the TH-D74 and it does everything but APRS. From this list it probably has the most bang for the buck since there are two ways of doing APRS: through D-Star repeaters that have it enabled or with an extra piece of kit that you plug to it. The audio quality seems not to be as good as the TH-D74 but it still is great. Like the TH-D74, this is a radio to buy, keep and don’t think about equipment for many years (at least not hand-helds).

    Icom ID-51A PLUS2

    After these two, we get into the get a radio now with the intention of upgrading later.

    Icom IC-E91

    Icom IC-E91The Icom IC-E91 cost less than half of the TH-D74 but I’m a bit puzzled by it. I cannot find a lot of reviews or information about it. It doesn’t seem to be a popular unit. It can do D-Star but to transmit GPS it requires an external GPS module that would put it close to the cost of an ID-51 PLUS2. When it comes to APRS, it’s the same story as for the other Icom on this list.

    Yaesu FT-60

    Yaesu FT-60The Yaesu FT-60 is a good solid radio. It cost about a sixth of the TH-D74 and it only ticks the VHF/UHF boxes on my requirements so this is definitely one to get started and upgrade later.

    A Yaesu FT-411E was my first radio and I always had a soft spot for Yaesu, so it saddens me that they decided to make their own proprietary protocol for digital radio. This is why you don’t see any higher end Yaesu models, I don’t want to use nor support System Fusion. I really hope one day Yaesu will start producing equipment with D-Star, then I will consider again buying their higher end models.

    A Baofeng

    baofeng's messy product line in the uk

    This would cost me the same as a pizza. It’s definitely in the buy something to get started and upgrade later. From a cost point of view, this is a no-brainier. I could just go ahead and buy one or two just for the lolz. I do have two issues with it:

    The first is quality. Obviously I don’t expect high end quality, but I read some reports of horrible things. I don’t want to buy a paperweight.

    The second issue is that I don’t know which one to get. Their line of products is a mess. In the UK they have a gazillion different models. They changed names three times (Pofang, Misuta). Their website is confusing and once I drill down on all the models, they seem to be all the same. If you look at their American presence, there they offer two radios with clear pros and cons, none of which are available in the UK.

    Conclusion

    Well, I don’t have one. I haven’t made a decision yet. I’d like to just buy a Baofeng now and upgrade later, but it sounds like I’m buying a problem. Getting the Yaesu seems a bit expensive for an upgrade-later path (but not too bad). Getting any of the high ends feel like throwing money for a toy instead of taking on a new hobby in a sensible manner.

    Any word of advice?

  • I’m evaluating re-writing Dashman in Java (instead of Electron). It’s too early to tell, but so far all the experiments and the early re-write are going well and it’s solving the issues I was having with Electron. This short post is about CircleCI.

    Because now I’m using common, boring technologies, I decided setting a CI was going to be easy enough that I didn’t mind doing it and I chose CircleCI, mostly because of their free plan.

    Sadly, my project didn’t work out of the box. JavaFX was nowhere to be found. The reason was that CircleCI uses OpenJDK instead of the Oracle’s JDK by default. I guess that’s enough for the vast majority of people. Switching to Oracle’s JDK was not hard, but I couldn’t find a straightforward post, so, this is it.

    CircleCI gives you a configuration example that contains something like this:

    [code]
    version: 2
    jobs:
    build:
    docker:
    # specify the version you desire here
    – image: circleci/openjdk:8-jdk
    [/code]

    I’m not very familiar with Docker and how CircleCI uses, all I know is that it’s a container system for, essentially, operating system images. CircleCI has a repo of Docker images and the one that include’s Oracle’s JDK is called circleci/jdk8. What comes after the colon in the config is the version of the Docker image (I think) which Docker calls tags and for this one, at the time of this writing, there was 0.1.1 but you can easily check the latest one here: https://hub.docker.com/r/circleci/jdk8/tags/

    With that in mind, the config should now look like this:

    [code]
    version: 2
    jobs:
    build:
    docker:
    # specify the version you desire here
    – image: circleci/jdk8:0.1.1
    [/code]

    And that’s it… well, unless you are using Gradle. That image doesn’t provide Gradle, so, you need to replace all the mentions of gradle with gradlew and use the wrapper that should be part of your repo. But before that will work, you need to make that file executable on the CircleCI server by calling chmod +x gradlew on it. The resulting config file for me looks like this:

    [code]
    # Java Gradle CircleCI 2.0 configuration file
    #
    # Check https://circleci.com/docs/2.0/language-java/ for more details
    #
    version: 2
    jobs:
    build:
    docker:
    # specify the version you desire here
    – image: circleci/jdk8:0.1.1

    # Specify service dependencies here if necessary
    # CircleCI maintains a library of pre-built images
    # documented at https://circleci.com/docs/2.0/circleci-images/
    # – image: circleci/postgres:9.4

    working_directory: ~/repo

    environment:
    # Customize the JVM maximum heap limit
    JVM_OPTS: -Xmx3200m
    TERM: dumb

    steps:
    – checkout
    – run: chmod +x gradlew

    # Download and cache dependencies
    – restore_cache:
    keys:
    – v1-dependencies-{{ checksum "build.gradle" }}
    # fallback to using the latest cache if no exact match is found
    – v1-dependencies-

    – run: ./gradlew dependencies

    – save_cache:
    paths:
    – ~/.m2
    key: v1-dependencies-{{ checksum "build.gradle" }}

    # run tests!
    – run: ./gradlew test
    [/code]

    And that’s it, that worked.

  • Searching online for how to set up the credentials to access the database (or any other service) while in development leads to a lot of articles that propose something that works, but it’s wrong: putting your credentials in the application.properties file that you then commit to the repository.

    The source code repository should not have any credentials, ever:

    • You should be able to make your project open source without your security being compromised.
    • You should be able to add another developer to your team without them knowing any credentials to your own development machine.
    • You should be able to hire a company that does a security analysis of your application, give them access to your source code and they shouldn’t gain access to your database.
    • You should be able to use a continuous integration service offered by a third party without that third party learning your database credentials.

    If you want to see what happens when you commit your credentials to your repo, check out these news articles:

    That’s probably enough. I hope I convinced you.

    In an effort to find a solution for this, I asked in Stack Overflow and I got pointed in the right direction.

    Leave application.properties where it is, in your resources of code folder, commit it to the repository. Instead, create a new file in ${PROJECT_ROOT}/config/application.properties and also add it to your version control ignore file (.gitignore, .hgignore, etc). That file will contain the credentials and other sensitive data:

    # This should be used only for credentials and other local-only config.
    spring.datasource.url = jdbc:postgresql://localhost/database
    spring.datasource.username = username
    spring.datasource.password = password

    Then, to help onboard new developers on your project (or yourself in a new computer), add a template for that file, next to it. Something like ${PROJECT_ROOT}/config/application.template.properties that will contain:

    # TODO: copy this to application.properties and set the credentials for your database.
    # This should be used only for credentials and other local-only config.
    spring.datasource.url = jdbc:postgresql://localhost/database
    spring.datasource.username =
    spring.datasource.password =

    And voila! No credentials on the repo  but enough information to set them up quickly.

    Disclaimer: I’m new to Spring Boot, I only started working with it a few days ago, so, I may be missing something big here. If I learn something new that invalidates this post, I’ll update it accordingly. One thing I’m not entirely sure about is how customary it would be to have ${PROJECT_ROOT}/config/application.properties on the ignore list. Please, leave a comment with any opinions or commentary.

  • There’s already an AJAX re-frame effect handler provided by Day8, the same guy who made re-frame and there’s nothing wrong with it. From the documentation, this is how you use it:

    [code lang=”clojure”]
    (re-frame/reg-event-fx ::http-post
    (fn [_world [_ val]]
    {:http-xhrio {:method :post
    :uri "https://httpbin.org/post&quot;
    :params data
    :format (ajax/json-request-format)
    :response-format (ajax/json-response-format {:keywords? true})
    :on-success [::good-post-result]
    :on-failure [::bad-post-result]}}))
    [/code]

    That felt a little to verbose for my taste, so I made my own that you use like this:

    [code lang=”clojure”]

    (re-frame/reg-event-fx ::http-post
    (fn [_world [_ val]]
    {:ajax {:post "https://httpbin.org/post&quot;
    :params data
    :on-success [::good-post-result]
    :on-failure [::bad-post-result]}}))
    [/code]

    If you are familiar with cljs-ajax, you’ll notice the pattern. Day8’s solution uses the raw cljs-ajax API, while I use the higher level one. Day8’s is more versatile, mine is more compact.

    This is my solution:

    [code lang=”clojure”]
    (ns ajax-fx-handler
    (:require [ajax.core :as ajax]
    [re-frame.core :as re-frame]
    [clojure.set :as set]))

    (re-frame/reg-fx :ajax
    (fn http-effect [request]
    (let [request-verb (set/intersection #{:get :post :put :patch :delete :head :options :trace :purge}
    (set (keys request)))]
    (if (not= 1 (count request-verb))
    (throw (js/Error (str "When specifying an AJAX request, one and only one verb should be specified. Found: " request-verb)))
    (let [request-verb (first request-verb)
    url (get request request-verb)
    request (if (contains? request :on-success)
    (assoc request :handler #(re-frame/dispatch (conj (:on-success request) %)))
    request)
    request (if (contains? request :on-failure)
    (assoc request :error-handler #(re-frame/dispatch (conj (:on-failure request) %)))
    request)
    request (-> request
    (dissoc request-verb)
    (dissoc :on-success)
    (dissoc :on-failure))
    ajax-fn (cond
    (= request-verb :get) ajax/GET
    (= request-verb :post) ajax/POST
    (= request-verb :put) ajax/PUT
    (= request-verb :patch) ajax/PATCH
    (= request-verb :delete) ajax/DELETE
    (= request-verb :head) ajax/HEAD
    (= request-verb :options) ajax/OPTIONS
    (= request-verb :trace) ajax/TRACE
    (= request-verb :purge) ajax/PURGE)]
    (ajax-fn url request))))))
    [/code]

    Feel free to use it, but, is it worth releasing it as a library? does anybody want that?

  • Jacob Emcken, the author of aws-simple-sign, reached out to me to share that his library can probably help with this, so if you need to achieve this goal, you might want to check it out.

    Disclaimer: I haven’t tried it or looked at its code.

    Dashman uses S3 for storing the encrypted screenshots. They can go directly from the renderer application to S3 and from there to the displayer application as there’s no need for the server application to ever see them. They are end-to-end encrypted anyway.

    My needs in this regard were a bit unique. I have a server component that can generate whatever tokens and signatures are needed and then send it to both applications. The applications are written in ClojureScript, so, essentially, JavaScript, but unlike most direct-to-S3 upload solutions, I don’t have a web site with a form, so, I don’t need to fiddle with CORS or forms.

    The two libraries

    I found two libraries that could have helped me. The big one is amazonica, a Clojure library to interact with AWS. It looks very complete but when I tried to use the com.amazonaws.services.s3/generate-presigned-url function, I got this error:

    The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.

    There are two versions of signatures, version 2 and 4. Version 2 is deprecated, it might or might not work on some data centers but it definitely doesn’t work on all. I refused to believe it at first because I was using very recent version of all libraries, but yup, it was generating version 2 of the signature with signed URLs that look something like this:

    https://bucket-name.s3.amazonaws.com/key-name?AWSAccessKeyId=AKI...OA&Expires=1494190327&Signature=ct...3D

    Digging further I found this snippet of code:

    (System/setProperty SDKGlobalConfiguration/ENFORCE_S3_SIGV4_SYSTEM_PROPERTY "true")

    But for me, it made no difference in my case. For the record I reported the issue and it is supposed to work, but it doesn’t for me.

    Then there’s s3-beam, that looked promising, but it seems targeted towards forms, not just plain PUT requests from the clients. On top of that, I didn’t want to mess with async, I didn’t need it and it would be over-engineering for my case. Lastly, it uses it’s own custom way of creating the signatures instead of Amazon’s SDK and that code looks like it’s generating the old signatures (sha1 instead of sha256). I ended up not even trying it.

    Instead, I decided to do my own custom option, which was not hard and worked straight away. This post is about sharing that code.

    Custom solution using the AWS Java SDK

    Starting on the server/Clojure side of things, I added the dependency:

    [com.amazonaws/aws-java-sdk "1.11.126"]

    With only that I was getting this unexpected error when trying to use the AWS SDK:

    ERROR com.fasterxml.jackson.databind.ObjectMapper.enable([Lcom/fasterxml/jackson/core/JsonParser$Feature;)Lcom/fasterxml/jackson/databind/ObjectMapper;
    java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.ObjectMapper.enable([Lcom/fasterxml/jackson/core/JsonParser$Feature;)Lcom/fasterxml/jackson/databind/ObjectMapper;
    ERROR Could not initialize class com.amazonaws.partitions.PartitionsLoader

    which I resolved by adding these other dependencies as well (mostly about getting the latest version):

    [com.fasterxml.jackson.core/jackson-core "2.8.8"]
    [com.fasterxml.jackson.core/jackson-databind "2.8.8"]
    [com.fasterxml.jackson.core/jackson-annotations "2.8.8"]

    I wrote a function to generate the S3 client:

    (defn aws-s3-client []
      (let [region (Regions/fromName (config/env :aws-s3-region))
            credentials (BasicAWSCredentials. (config/env :aws-access-key-id)
                                              (config/env :aws-secret-access-key))]
        (-> (AmazonS3ClientBuilder/standard)
            (.withRegion region)
            (.withCredentials (AWSStaticCredentialsProvider. credentials))
            .build)))

    config/env just fetches configuration values, powered by cprop and mount. This is just standard stuff from Luminus. With

    (defn- pre-signed-url [key & [verb]]
      (let [expiration (tc/to-date (-> 24 t/hours t/from-now))
            generate-presigned-url-request (doto (GeneratePresignedUrlRequest. (config/env :aws-s3-bucket-name) key)
                                             (.setMethod (if (= verb :put)
                                                           HttpMethod/PUT
                                                           HttpMethod/GET))
                                             (.setExpiration expiration))]
        (.toString (.generatePresignedUrl (aws-s3-client) generate-presigned-url-request))))

    The function generates either a URL for reading, :get, or for writing, :put, depending on the parameter verb. key is just the path part of the URL (remember, S3 is actually a key/value storage). All URLs will last for 24hs, something you can tweak as well.

    And that’s it, generating the URL is done, now we have to use it.

    Reading and writing from ClojureScript

    If you have a form, then, you might need to deal with CORS and other issues. I suggest you look at s3-beam at least for inspiration. For me, it was just a matter of generating the requests from the ClojureScript app, which uses cljs-ajax. Reading was straightforward enough:

    (ajax/GET get-url
              {:handler (fn [value]
                          (do-something value))})

    although the handler might have to deal with encoding issues. Writing is similar, but again, you may need to fiddle with encoding issues. Since what I was sending is a JSON payload (an encrypted screenshot), this worked for me:

    (ajax/PUT put-url
              {:format :json
               :params payload})

    If you are uploading binary data, I recommend you play with :format :raw and the :body attribute.

  • Dashman is composed of many components  including three desktop apps written in ClojureScript using Electron that share code through a private library (as in, it’s not open source).

    To have continuous integration and a set up that is easy to boostrap, that library should be deployed to a private repository. I achieved that using the plug in s3-wagon-private so that the project.clj of the common library looks like this:

    [code lang=”clojure”]
    :repositories [["private" {:url "s3p://s3-bucket-for-my-project/releases/"
    :username "not-telling-you"
    :passphrase "not-telling-you-either"
    :sign-releases false}]]

    :plugins [;…
    [s3-wagon-private "1.3.0"]]
    [/code]

    You should never have production or even staging credentials on your source code repository; but those credentials are neither. They allow to compile the project so, pretty much everybody that has access to the source code also needs access to that private repository. Having them in project.clj simplifies CI, on-boarding developers, etc.

    One you have that you can deploy the library by executing:

    [code lang=”bash”]
    lein deploy private
    [/code]

    The project of the apps using the common library look similar:

    [code lang=”clojure”]
    :repositories [["private" {:url "s3p://s3-bucket-for-my-project/releases/"
    :username "not-telling-you"
    :passphrase "not-telling-you-either"
    :sign-releases false}]]
    :dependencies [;…
    [tech.dashman/clientcommon "0.1.0-SNAPSHOT"]]

    :plugins [;…
    [s3-wagon-private "1.3.0"]]
    [/code]

    Yes… very similar.

    The next issue is compiling and re-loading code. You could modify the common library, install it, re-compile the apps, and re-run the apps. And you could do it while wearing a mullet and watching MacGyver (the original one). This is not the 80s! You want code reloading and all the goodies.

    If you are using Clojure, you should look into checkouts and maybe lein-checkout. In ClojureScript you can just add the path of the source code to your project even if it’s outside the project, with one gotcha. You might be used to a config like this:

    [code lang=”clojure”]
    :cljsbuild {:builds {:app {:source-paths ["src/app"]}}}
    [/code]

    which would imply this might work:

    [code lang=”clojure”]
    :cljsbuild {:builds {:app {:source-paths ["src/app" "../clientcommon/src"]}}}
    [/code]

    It might on non-Windows platforms, on Windows it doesn’t. On Windows you want this:

    [code lang=”clojure”]
    :cljsbuild {:builds {:app {:source-paths ["src/app" "..\\clientcommon\\src"]}}}
    [/code]

    Since you should probably never commit that anyway, it’s not a big deal.

  • I’m developing Dashman in ClojureScript (and Clojure) and as I was developing the cookie handler I found sites that constantly modify the cookies. As in, many times per second. Obviously I don’t want to encrypt the cookies and send them to the server that often, but even without taking that into account, just storing them to keep track of them in an atom, as in:

    (reset! cookies new-cookies)

    was slowing down the app considerably. I found myself needing to debounce so many calls as there’s no value in the middle calls, only the last one. Let’s backtrack a little bit here.

    The term to debounce comes from electronics:

    To remove the small ripple of current that forms when a mechanical switch is pushed in an electrical circuit and makes a series of short contacts.

    In programming it refers to a signal that gets emitted often and you want to process it but you don’t care about processing every intermediate value. To tie into electronics imagine you have a program that receives a message every time the temperature changes and the message contains all the temperature changes for the day. Clearly each message doesn’t have to be processed, only one, only the latest.

    Here’s where a software debouncer can help. It’s a function that gets a function and returns another function. When you call the resulting function 10 times, it will only call the original function once, with the last value it received.

    I found this debouncer written by David Nolan: https://gist.github.com/swannodette/5888989 and in the comments you can start to see that writing a debouncer might not be a trivial as you may think. One of the comments point to this other version: https://gist.github.com/scttnlsn/9744501. The comment implies this is a more correct version but I haven’t verified it myself. In that last page you can see someone found a bug and posted an improved version.

    For me, when a supposedly simple code snippet generates so many comments and versions, it’s better to find a library that implements it than to copy and paste and never maintain it again. Unfortunately, I couldn’t find a ClojureScript debouncer library but I got pointed to goog.function.debounce which you can use this way;

    (ns whatever
      (:require [goog.functions]))
    
    (def debounced-println (goog.functions/debounce println))