Introduction

What is jimmutable?

jimmutable is both a lightweight library and a "way of thinking" that is intended to make the authorship of java code for the cloud easier, more free of mistakes, and, simply, more fun.

Although a general purpose java library, jimmutable is particularly well suited to the writing of cloud (AWS) hosted micro-services.

Key Features

  • Don’t make me think
    jimmutable saves you mental energy by laying down simple, easy to follow patterns that are designed to make the error free writing of code easy

  • Immutability Made Easy
    jimmutable, as the name implies, is patterned around easy to write and maintain immutable java objects

  • Easy Unit Tests
    jimmutable embraces unit testing at a root level and facilitates the easy creation of unit tests. Of particular importance is the easy creation of unit tests for serialization (often a sore point)

  • Easy Backwards Compatibility
    jimmutable makes it easy to extend an object while retaining the ability to read "old" versions of the object

  • Performance
    Speed and low memory footprint are a central part of the design

  • Loved by System Administrators
    Much thought and consideration is given to the poor souls that must toil in the never ending effort to keep our apps up. We are forever in your debt. jimmutable tries to make your life easier

Can I use jimmutable in my project?

Almost certainly. All of jimmutable is licensed under the 3-clause BSD license. If that is not permissive enough, what is?

So, fork it, port it, use it commercially…​ basically, have at it!

Where can I get jimmutable?

Don’t make me think

"Don’t make me think" is one of the core tenants of jimmutable. The theory goes something like this: each day we wake up and greet the world. At this moment of waking, we posses only a finite amount of mental energy. Think of it like a book of matches. The solution of each and every mental problem we confront during the day burns one of these matches. When our matchbook is empty, we are done. Time to go fishing, play golf, go for a walk, anything but more mental effort. Trying to work more after our matchbook is gone is just like trying to start a fire with no matches — you can do it, but its way easier with matches.

If you accept this state of affairs as being consistent with the human condition (as this author does) than one thing you can do to make your life as a programmer better is to write your code in a way that does not constantly present you with mental puzzles.

How does this happen? I think a simple example will help shed light on things. Imagine, if you will, a function whose purpose is to pretty print (neatly indent etc.) XML. Outside of jimmutable, one common method signature for such a function might be:

String prettyPrintXML(String unformatted_xml)

What is so hard about that? Well, let me ask you something, based on that method signature, what does prettyPrintXML do if the input is null? What does it do if unformatted_xml is not a valid XML document? Does it throw an unchecked exception? If so, which one(s)? Does it simply return its input? It is, actually, a touch of a puzzle.

Now, sure, you can go and read the JavaDoc for the function. Maybe the author will have been good enough to clearly document the error conditions. Maybe not, so…​ ctrl click in eclipse and onto the source code…​ DOH! I don’t have the source jar installed. Ok…​ ummm…​ well, lets write a short little program to test it…​ BLAM! One match burned.

How to avoid thinking in jimmutable

jimmutable is designed to be used "in the heat of battle". You know, you have your tunes blaring in your headphones and you are cranking out code. You are getting things done! You don’t have time to check no stinking error conditions, you have sh!t to do! jimmutable is great for this because code written in this style is written with a commitment never to make you think. This is achieved by always following a pattern. One of jimmutable’s patterns is that the error behavior of a method will always be trivially understandable from its method signature (which you can’t help but see from autocomplete in eclipse while you are pounding klocs (thousands of lines of code) out.

  1. Method signatures that tell you (most) everything you need to know about how a function works

  2. Throw unchecked exceptions when the truly unusual happens

  3. Immutable objects make life easy and code safe

Method signatures in jimmutable

In jimmutable, if you see a method signature like this:

String prettyPrintXML(String unformatted_xml)

Then it means: do you worst. No matter what you give me as an argument: null, JavaScript, the complete text of war an peace, whatever, I will work and give you the right answer. I hope it is immediately obvious to you, dear reader, that the prettyPrintXML function does not have a snowballs chance in hell of being this rugged. As a result, it has a method signature of

String prettyPrintXML(String xml, String default_value)

(You can see it yourself, it is a real function in JavaCodeUtils)

What does a signature like this mean? It means, give me anything you want for xml. I will try to pretty print it. If I can’t make this happen (hey, I can’t pretty print the text of war a peace) I will return default_value. I won’t throw an exception (checked or unchecked). I won’t print a bunch of a garbage to System.out. I will, simply, return default_value

Conventions for getters

Imagine that you have a jimmutable class, Book. This is shockingly easy to imagine as there is, in fact, a Book class implemented in org.kane.base.examples, but I digress. A Book has several fields, one of which is title. As we might expect, Book implements a method to get the title. Its signature is as follows:

String getSimpleTitle()

Whenever you see a getSimpleXXX in jimmutable, you know a couple of things:

  1. A non-null value will always be returned

  2. Nothing too bad can happen (no exceptions, infinte loops, many seconds of computation, etc.)

Now, some books have an ISBN (International Standard Book Number) (Yes, virginia, there are books in the world that do not have a ISBN). The method to get ISBN, therefore, looks like this:

String getOptionalISBN(String default_value)

You probably have already stopped thinking, and this is a good thing! You know, intuitively, what this means. For the record, I will share with you that, whenever you see getOptionalXXX in jimmutable:

1.) The field you are getting may be set, or it might be unset 2.) If the field is set, then the function will, trivially, return the value to you 3.) If the field is not set, then the function will return default_value to you 4.) The function won’t take any significant amount of resources to execute, and won’t do anything too bad (throw an exception etc.)

Complicated functions

Ok, so, what if I want to write a function that is truely complicated. Something like, for a book, examine the text and tell me if the book is in iambic pentameter (note: for the record, I have no idea how to implement this function). In that case, the function signature will have the word Complex in it. For example:

Boolean getComplexIsIambicPentameter(Boolean default_value)

or

Boolean computeComplexIsIambicPentameterByExaminingProse(Boolean default_value)

What do we know when we see Complex?

1.) This function requires some thought 2.) It might take a while to run 3.) It might not work — when it does not work, it will return default_value

Dealing with the truly unusual

As we merrily code along, we often find ourselves thinking "Should I check for …​" followed by "nah, only someone who is truly braindead would do that. For example, we all know that we should validate every input to every function we write. Because…​ Murphy’s law. Sometimes this flows very naturally. For example, imagine that you are writing a function that reads small files from disk, returning the files bytes. It is pretty obvious that a good method signature for this might be:

byte [] getFileBytes(File src, byte default_value[])

And this is all pretty natural. If src happens to either not exist or not be a file, return default_value and all is well. This also gives us a natural out for (a truly weird) case: src is null.

Now imagine, dear reader, that you are coding a new class BookUtils and it implements a function that checks to see if the title of a book is a palindrome (the same spelled forward or backward) (For example, Seveneves by Neal Stephenson, ISBN 0062334514)

As you start to code this function, all seems well…​

static public boolean isTitlePalindrome(Book book)
{
    String title = book.getSimpleTitle();
    String title_backwards = new StringBuilder(title).reverse().toString();
    return title.equals(title_backwards);
}

Ahh, but now you have a problem. What if, just what if, some jerk decides to pass in a null book. This is, to put it mildly, super crazy unlikely. Its probably only going to happen in development etc. and its going to make our code dumb to always have to have a default value etc. for such an edge case. Don’t worry — in this case, jimmutable uses unchecked exceptions to "get those jerks right back". Here is how you code this:

static public boolean isTitlePalindrome(Book book)
{
     Validator.notNull(book);

     String title = book.getSimpleTitle();
     String title_backwards = new StringBuilder(title).reverse().toString();
     return title.equals(title_backwards);
}

The jimmutable unchecked exceptions

  1. ValidationException
    Throw a ValidationException whenever something is "not valid". Frequently thrown when a function is passed invalid parameters

  2. SerializeException
    Throw when serialization goes bad. Unable to fully read a file full of objects? Network connection fails in the middle of downloading a file? Throw a SerializeException If serialization works, but the underlying data is not valid, throw a ValidationException

  3. ImmutableException
    Thrown when someone tries to change something after an object has been completed

Standard immutable objects

By default, objects in jimmutable are robust and well behaved. Any object that extends StandardImmutableObject:

  • is immutable (after construction)

  • is serializable to and from XML

  • is serializable to and from JSON

  • is deep-clonable (simply call deepClone)

  • has robust support for data normalization

  • is checked for data validity every time an object is created/de-serialized

  • has a proper implementation of hashCode

  • has a fast implementation of equals

  • has a fast implementation of compareTo

  • is easy to write a unit test for (see the various unit test examples and the toJavaCode function)

This helps you avoid thinking on several fronts:

  • Is this object thread safe? Of course…​ its immmutable

  • Is it safe to return a reference to this object? Of course…​ its immutable

  • Can I add this object to a Set? HashSet? TreeSet Of course…​

  • Can this object be the key of a Map? Of course!

  • Can I duplicate this object? Yes! But no real need…​ its immutable…​

Objects are either abstract or final

In jimmutable, any class you make should either be abstract or final. This saves you a whole ton of mental energy.

How? Well, lets put it this way, designing classes that can safely be extended is super difficult. Oh, sure, you may think you have, without thinking about it, made a class that is trivially extendable…​ but you probably have not. Is it thread safe? Can it serialize/de-serialize? Does it have weird construction errors? What happens when someone extends it that does not know what the heck they are doing?

It is, basically, impossible, to create on unintentionally. Therefore, we save ourselves from having to think about this by making it explicit: if we are designing a class that can be extended we declare it abstract. Otherwise, it is final.

I can hear the objections already: this breaks OOP! I can see the examples: Shape, Rectangle extends Shape, Square Extends Rectangle. To which is say: no, its easier to have Shape, AbstractRectangle extends Shape, final Rectangle extends AbstractRectangle, final Square exetends AbstractRectangle. Trust me.

Immutable objects in jimmutable

So, you want to make a new immutable object in jimmutable. Easy, either extend StandardImmutableObject or Stringable

StandardImmutableObject vs Stringable

You should extends Stringable whenever you are creating a class that is easily serialized to (and de-serialized from) a String. For example, see BrandCode and/or PartNumber in jimmutable’s examples. Both simply "wrap", and limit the allowable contents of, a String. Therefore, they extend Stringable.

BTW: Stringable iteself simply extends StandardImmutableObject. So…​ all it really does at the end of the day is save you some typing. That being said, please use Stringable when applicable.

The lifecycle of an immutable object

Every immutable objects starts life as a mutable one. When you think about it, this simply must always be the case — otherwise, how would you set any of the fields during construction? Outside of the construction chain, however, every StandardImmutableObject is immutable. This means that, no matter what you do, it is not possible to obtain a reference to a StandardImmutableObject that is not immutable.

How does an object make the transition from mutable to immutable? Simple: a call is made to complete()

complete() performs the following actions, in exactly the following order:

  1. normalize()

  2. validate()

  3. freeze()

When you need to call complete vs when complete is called for you

If you create your own, non serialization constructor, you need to (once you are done modifying the object being built) call complete yourself.

When objects are created via de-serialization (i.e. StandardObject.deserialize), then complete is called for you. Since your de-serialziation constructor (the constructor that takes an ObjectParseTree as its only parameter) is invoked as part of the de-serialization process, you do not need to call complete in this constructor (indeed, that is a big no-no)

When an object is cloned (deepClone) it is serialized and de-serialzied, therefore complete is called for you.

Because our standard builder pattern (more on this later) uses deepClone, there is no need to call complete when building an object using a builder (it is called for you via deepClone)

Virtualized Backplane

To aid in development for Jimmutable apps, we utilize a virtualized intance of the backplane. The backplane consists of Elasticsearch, Kibana, and Redis, and all of these live on a VM using VirtualBox. This allows us to simply deploy an image and start up the VM to be ready to develop web applications, rather than having to create local installations and maintain versions on an individual basis.

List of Installed Services on Image

  • Amazon Linux 2 AMI

  • Java 1.8

  • Elasticsearch 7.6.1 (port 9200 and 9300)

  • Kibana 7.6.1 (port 5601)

  • Redis 4.0.14 (port 6379)

All running on local host (127.0.0.1)

Step 1: Enable Virtualization On Your Computer

In order for VirtualBox to be fully operational, your computer must be setup to allow for virtualization. If your computer is already set up to do so, feel free to skip this step.

Windows PCs

You will need to configure your processor in the BIOS to allow for virtualization. Open the BIOS and find “Intel Virtualization” and enable it. Save your changes and boot up your computer as normal.

Mac

Virtualization support for your processor should be enabled by default. Skip this step if that's the case, otherwise use this handy guide

Step 2: Download and Install VirtualBox

Visit VirtualBox's download page and download the latest VirtualBox client for your OS. When running through the installer, choose all the default options.

Step 3: Download the Virtual Backplane Image

We've created a VirtualBox VM Image (.OVA) file to use for the VM. Download it here. Be careful, it's big (~1.3 GB).

Step 4: Import and Start the VM in VirtualBox

In VirtualBox, click File → Import Appliance. Select the image that you downloaded in Step 3. Leave the default options for the import process. Once completed, it should show up in the list of VMs available. Start it using the Start button (Alternatively, choose Normal Start).

A new window should appear with your Linux instance. Select the latest Amazon AMI build, and advance to the login screen. Your VM is now up and running. All services (Elasticsearch, Kibana, and Redis) have been configured to start on boot, and port forwarding allows them to be used as localhost instances. So your backplane is up and ready to go!

Step 5: Launch your Web App

When working with Elasticsearch, Kibana, and Redis, Jimmutable applications default to localhost and the associated ports (9200/9300, 5601, and 6379 respectively). This is what the VM has been configured for as well.

Launch your web application, and it should attempt to connect or use these services as if they were running on localhost.

Jimmutable Cloud currently uses the Elasticsearch Java High Level REST client. Be aware that the current client does not support the previous version (6.x) of Elasticsearch

Extra Debugging Info

Login

  • username: ec2-user

  • password: password

Operating the shell

The VM runs on Amazon Linux 2 AMI, which is a CentOS-based distro. There isn't a lot of specific documentation on Amazon Linux 2. When researching, it's best to include CentOS in your searches instead.

Service data locations

  • /usr/share/elasticsearch/

  • /usr/share/kibana

  • /usr/local/bin (Redis)

To modify configs

  • sudo nano /etc/elasticsearch/elasticsearch.yml

  • sudo nano /etc/kibana/kibana.yml

  • sudo nano /etc/redis/redis.conf

  • Be sure to restart the respective service

To restart services

  • sudo systemctl restart elasticsearch

  • sudo systemctl restart kibana

  • sudo systemctl restart redis

  • Alternatively, restart the whole VM

To check service status

  • sudo systemctl status elasticsearch

  • sudo systemctl status kibana

  • sudo systemctl status redis