Tool Showcase: Node-RED

Reading Time: 5 minutes

Node-RED is a flow-oriented tool to wire together hardware devices, APIs and online services. It is mainly targeting the IoT market but can be used for a lot of other things as well. Because of its easy to use browser-based UI and drag and drop programming system, it’s really beginner-friendly with a steep learning curve.

Even though it was developed by IBM in 2013, it’s not really known to most of the IT community. At least none of my colleagues knew it. That’s worth for me to write this tool showcase. Enjoy reading!

Getting started

Instead of just reading along, I encourage everybody to just start Node-RED and try it yourself. If docker is installed, this is just a matter of seconds. Use the following command to start a Node-RED instance locally:

docker run -it -p 1880:1880 --name mynodered nodered/node-red

That’s it. You are ready to go! Open your browser and go to localhost:1880 to access the Node-RED UI.

One of the simplest flows is the following one:

Drag an “http in”, “template” and “http out” node into the flow and connect them. After clicking the deploy button you can access localhost:1880/<whateverPathYouConfiguredInYourHttpInNode> to see whatever you’ve configured in your template node. Congratulations, you have just created your first Node-RED flow!

Of course, rendering static content on an endpoint is not the most exciting thing to do, but between the HTTP in and out nodes, you’re free to do whatever you want. Nodes to make HTTP calls to different URLs, reading and writing files and much more are provided by Node-RED by default.

The power of the community

Node-RED uses Node.js for its nodes (yes, the terminology “node” is overused in the Node-RED context 🙂 ). This has a big advantage, that new nodes can be added easily from the node package manager (npm). You can find these nodes by searching for “node-red-contrib” in the npm repository. An even simpler option is to install these nodes using the “Manage Palette” option in the UI. There you can install new nodes with a single click.

There are nodes for almost everything. Need support for slack? Yep, there’s a node for that. Tired of pressing light switches in your house to turn off and on your Philips Hue lights? Yep, there’s a node for that as well. Whatever you can imagine, there’s a node for it.

A slightly more advanced flow

To test some Node-RED features, I tried to come up with a slightly more complicated example. I own some Philips Hue lamps and a LaMetric Time. After searching some nodes for these devices, I was really surprised that somebody already built some for these two devices (I was especially surprised about the support for the not so well-known LaMetric Time).

My use case was pretty straight forward. Turn on the lights when it gets dark and display a message on my LaMetric near my TV. At midnight, turn off the lights and display a different message. Additionally, I wanted some web endpoints that I could call to trigger both actions manually.

After only a few minutes, I had the following flow:

And it works! I found a node that sends an event as soon as the sun goes down at my particular location. Very cool. All the other nodes (integration for Philips Hue and LaMetric) can also easily be added with the “Manage Palette” option in the GUI. All in all, the implementation of my example use-case was pretty straight forward and required no programming know-how.

Expandability

Even though there are almost 3000 community-contributed nodes available to use, you might have some hardware or API that does not (yet) have some pre-made nodes. In that case, you can implement your own nodes pretty easily. The only thing required is a text editor and some node.js know-how.

The Node-RED documentation provides a good guide on how to create custom nodes: https://nodered.org/docs/creating-nodes/first-node

It is highly recommended to push your custom nodes to the npm repository to be used by the community.

Additional Resources

There are a whole lot more features that are not described in this blogpost.

  • Flows are just .json files and can easily be imported or exported or added to a git repository
  • Flows can be converted to subflows and used like nodes in other flows
  • Multiple flows can run in parallel and trigger each other
  • There are special nodes for error handling or low-level TCP communication
  • There are keyboard shortcuts for everything
  • … and much more!

Feel free to have a look yourself:

Thanks for reading!

3 shell scripts to help you manage AEM Instances

Reading Time: 5 minutes

The Problem

Every AEM developers career starts by double-clicking the AEM jar file. And that’s where you can do your first mistake. AEM might shout at to you “Hey NOOB you need a license!”. You forgot to copy the license file next to the jar… *sigh*. But don’t worry, this happens to everybody.

After copying the license file, AEM will start and you will get a nice little GUI.

Congratulations! You are ready to go! You know where AEM is running (localhost:4502) and you even have a little stop button in the bottom left corner. But this is probably the only time you see this GUI. All the cool kids start AEM with the start script in crx-quickstart/bin/. But how do you know if AEM is running? How can you find out which port is used and how do you stop the instance?

This blogpost will provide you with three little shell scripts to address these problems and help you to manage your AEM instances without the need for a GUI.

#1 Get information about running AEM instances

To figure out which AEM instances are currently running you might smash something like this into your terminal window:

This method has several disadvantages. The command is hard to remember and the output is very hard to read.

Here’s a little challenge for you: Can you figure out the port, debug port and runmodes in the gif above before it loops? Give it a try.

And anyway, what does this command even mean?

Bonus Tip – Great page to explain shell commands (bookmark it!): https://explainshell.com/explain?cmd=ps+aux+%7C+grep+cq

Back to the topic. Our first script (“aeminfo”) provides a simple overview of all running AEM instances. It turns the complicated output into a readable form:

Nice! All you have to do is adding this script to your .bash_profile. The script does not use any external dependencies and should run many unix/linux based systems (tested on OSX and CentOS).

# Displays all running AEM instances
function aeminfo(){

    if [ "$(ps aux | grep q | grep crx)" ]
        then

            count=0;
            echo ""
            ps auxeww | grep q | grep sling | while read -r line ; do

                ((count++));
                params=($line);

                username=(${params[0]});
                pid=(${params[1]});
                port="not found";
                debugPort="not found";
                xmx="not found";
                runmodes="not found";
                path="not found";

                portRegex="-p ([0-9]+)";
                debugPortRegex="address=([0-9]+)";
                xmxRegex="-Xmx([^[:space:]]+)";
                runmodesRegex="-Dsling.run.modes=([^[:space:]]+)";
                pathRegex="PWD=([^[:space:]]+)";

                [[ $line =~ $portRegex ]] &amp;&amp; port="${BASH_REMATCH[1]}";
                [[ $line =~ $runmodesRegex ]] &amp;&amp; runmodes="${BASH_REMATCH[1]}";
                [[ $line =~ $debugPortRegex ]] &amp;&amp; debugPort="${BASH_REMATCH[1]}";
                [[ $line =~ $pathRegex ]] &amp;&amp; path="${BASH_REMATCH[1]}";
                [[ $line =~ $xmxRegex ]] &amp;&amp; xmx="${BASH_REMATCH[1]}";

                echo "----------------------";
                echo "AEM Instance" $count;
                echo "----------------------";
                echo "username:  "$username;
                echo "pid:       "$pid;
                echo "port:      "$port;
                echo "debugPort: "$debugPort;
                echo "memory:    "$xmx;
                echo "runmodes:  "$runmodes;
                echo "path:      "$path;
                echo "----------------------";
                echo "";

            done

        else
            echo "";
            echo "No running AEM instances found";
            echo "";
        fi
}

#2 Death to all AEM instances!

Our next script (“killaem”) is pretty small. It just shuts down a running AEM instance:

function killaem() {
    kill $(ps aux | grep 'rx-quickstart' | awk '{print $2}')
}

But experienced AEM developers might know that asking AEM politely to stop is sometimes not enough.

Especially when it’s Friday evening and you’re looking forward to a cold beer in your local pub. For this emergency situation you could use a slight variation of the script (“KILLAEM”) (notice the capitalisation!). This will kill your instance definitely. No matter what. The consequences might be that the instance will be in the same condition as you are after too many beers in said pub. Broken and unable to work. So be careful with this command (and with drinking for that matter!)

function KILLAEM() {
    kill -9 $(ps aux | grep 'rx-quickstart' | awk '{print $2}')
}

#3 Improved debugging

So you destroyed your instance and it won’t start properly? What to do next? Have a look at your log files!

If you use the regular “tail -f error.log” command you will quickly notice that AEM logs a lot of stuff. How could you ever find that one useful log entry in the haystack of unnecessary INFO logs? This “aemlog” script might help you! It displays different log levels with different colours:

function aemlog() {
    tail -fn 128 "$@" | awk '
    /SEVERE/ {print "\033[35m" $0 "\033[39m"}
    /ERROR/ {print "\033[31m" $0 "\033[39m"}
    /WARN/ {print "\033[33m" $0 "\033[39m"}
    /DEBUG/ {print "\033[30m" $0 "\033[39m"}
    !/SEVERE|ERROR|WARN|DEBUG/ {print $0 }
';}
You could even observe multiple log files at once. Just pass them as parameters to the command: aemlog error.log access.log audit.log

Conclusion

The three little scripts might not do very much but they are easy to set up, don’t need any third-party software and provide some useful functionalities.

Just copy all shell scripts from this page into your ~/.bash_profile and you are ready to go!

If you have any suggestions for improvements feel free to leave a comment!

How to get a service reference or BundleContext with no OSGi context

Reading Time: 2 minutes

Issue

In Adobe Experience Manager (AEM) projects developers are working a lot in services, filters, servlets and handlers. All of these classes are OSGi components and services using the Felix SCR annotations or the newer OSGi DS annotations. But sometimes you need an OSGi service or the BundleContext also in non OSGi / DS controlled class

Solution

You can use the OSGi FrameworkUtil [1] to get the reference to the bundle context from any object. The code below shows how to get reference to the BundleContext and the service.

Most of the time, you have the SlingHttpServletRequest ready to pass:

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;

import java.util.Objects;

public class ServiceUtils {

    /**
     * Gets the service from the current bundle context.
     * Return null if something goes wrong.
     *
     * @param <T>     the generic type
     * @param request the request
     * @param type    the type
     * @return        the service
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> T getService(SlingHttpServletRequest request, Class<T> type) {
        SlingBindings bindings = (SlingBindings) request.getAttribute(SlingBindings.class.getName());
        if (bindings != null) {
            SlingScriptHelper sling = bindings.getSling();

            return Objects.isNull(sling) ? null : sling.getService(type);
        } else {
            BundleContext bundleContext = FrameworkUtil.getBundle(type).getBundleContext();
            ServiceReference settingsRef = bundleContext.getServiceReference(type.getName());

            return (T) bundleContext.getService(settingsRef);
        }
    }
}

Or you just use the class that was loaded over a bundle classloader.

package com.sanitas.aem.core.utils;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;

public class ServiceUtils {
    
    /**
     * Gets the service from the current bundle context.
     * Return null if something goes wrong.
     *
     * @param <T>     the class that was loaded over a bundle classloader.
     * @param type    the service type.
     * @return        the service instance.
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> T getService(Class clazz, Class<T> type) {
        Bundle currentBundle = FrameworkUtil.getBundle(clazz);
        if (currentBundle == null) {
            return null;
        }
        BundleContext bundleContext = currentBundle.getBundleContext();
        if (bundleContext == null) {
            return null;
        }
        ServiceReference<T> serviceReference = bundleContext.getServiceReference(type);
        if (serviceReference == null) {
            return null;
        }
        T service = bundleContext.getService(serviceReference);
        if (service == null) {
            return null;
        }
        return service;
    }

}

[1] https://osgi.org/javadoc/osgi.core/7.0.0/org/osgi/framework/FrameworkUtil.html