code

Golang Equality & Type Aliases

The go language specification states:

In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

In other words only identical types are comparable. Take the following code:

package main

import "fmt"

func main() {
	var x int32 = 3
	var y int64 = 3
	fmt.Printf("x == y is %v\n", x == y)
}

Attempting to compile the above program (goplay) will result in an error:

prog.go:8: invalid operation: x == y (mismatched types int32 and int64)

The compilation fails because x and y are of types int32 and int64 respectively. However, what if the addresses of x and y are compared instead?

package main

import "fmt"

func main() {
	var x int32 = 3
	var y int64 = 3
	
	var px *int32 = &x
	var py *int64 = &y
	
	fmt.Printf("px == py is %v\n", px == py)
}

Once again, using goplay, compilation fails:

prog.go:12: invalid operation: px == py (mismatched types *int32 and *int64)

The code fails to compile because golang’s type system is composed of two basic types – named and unnamed. A named type is something like string or int32. An unnamed type is any named type with a type literal applied to it. For example, the type *int32 is the named type int32 with the * (pointer) type literal applied.

So the reason the above example fails when comparing pointers is because golang doesn’t consider the pointer, a type literal, when deciding whether types are identical and therefore comparable. Only the named, or underlying, types are considered. In the case of *int32 and *int64 that would be int32 and int64.

For what it’s worth, the above code, had it compiled, would have printed the following:

px == py is false

The reason the comparison is false is because the addresses of the integer values are being compared, not the values themselves. The example was simply to show that the compilation itself fails due to the way golang looks at the underlying type when deciding whether two types are comparable.

So what does all this have to with type aliases? Well, let’s look at another example:

package main

import "fmt"

type Int32 int32

func main() {
	var x int32 = 3
	var y Int32 = 3
	
	fmt.Printf("x == y is %v\n", x == y)
}

In golang, an aliased type is more than an alias, it’s an entirely new type. That is why the above example fails to compile (goplay) with the following error:

prog.go:11: invalid operation: x == y (mismatched types int32 and Int32)

Just like the previous comparison of an int32 and int64, the types int32 and Int32 are different, and thus not comparable.

However, what happens if type literals, such as pointers, are thrown back into the mix?

package main

import "fmt"

type pInt32 *int32

func main() {
	var x int32 = 3
	var y int32 = 3
	
	var px *int32 = &x
	var py pInt32 = &y
	
	fmt.Printf("x == y is %v\n", *px == *py)
	fmt.Printf("px == py is %v\n", px == py)
}

Executing the above program with goplay produces the following output:

x == y is true
px == py is false

Success! The compilation succeeded and the program produced the expected results (please note that px != py, and that’s expected as x and y are not pointing to the same address).

Although to be fair, the very fact that py was assignable to the address of y dictated that the program would compile before it reached the heretofore problematic fmt.Printf... line. And that’s exactly why the program compiled — because of the fact that py was assignable to the address of y. As the go language specification states:

A value x is assignable to a variable of type T if:

  • x‘s type is identical to T.
  • x‘s type V and T have identical underlying types…

Which aligns with the tl;dr at the top of this post – only identical types are comparable.

It’s just that in golang, what makes one type identical to the next may not be so readily obvious.

Once the idea of how type aliases fit into equality with regards to go, they become quite useful when redefining/aliasing types from external libraries or versioned packages. Let’s use this source file from EMC{code}’s XtremIO adapter as an example:

package goxtremio

import xms "github.com/emccode/goxtremio/api/v3"

type Initiator *xms.Initiator
type InitiatorOptions xms.PostInitiatorsReq
type NewInitiatorResult *xms.PostInitiatorsResp

//GetInitiator returns a specific initiator by name or ID
func (c *Client) GetInitiator(id string, name string) (Initiator, error) {
	i, err := c.api.GetInitiator(id, name)
	if err != nil {
		return nil, err
	}
	
	return i.Content, nil
}

//GetInitiators returns a list of initiators
func (c *Client) GetInitiators() ([]*Ref, error) {
	i, err := c.api.GetInitiators()
	if err != nil {
		return nil, err
	}
	return ToRefArray(i.Initiators), nil
}

//NewInitiator creates a volume
func (c *Client) NewInitiator(opts *InitiatorOptions) (NewInitiatorResult, error) {
	pireq := xms.PostInitiatorsReq(*opts)
	pires, err := c.api.PostInitiators(&pireq)
	if err != nil {
		return nil, err
	}
	
	nir := NewInitiatorResult(pires)
	return nir, nil
}

//DeleteInitiator deletes a volume
func (c *Client) DeleteInitiator(id string, name string) error {
	return c.api.DeleteInitiators(id, name)
}

The above source imports the github.com/emccode/goxtremio/api/v3 as xms and then aliases the following types:

type Initiator *xms.Initiator
type InitiatorOptions xms.PostInitiatorsReq
type NewInitiatorResult *xms.PostInitiatorsResp

This enables the ability to mask internal, versioned types with ones specifically designed to be exported.

Note that the type InitiatorOptions is not an alias to a pointer, but to a named type. The reason for this is that the function c.api.PostInitiators requires a pointer to an xms.PostInitiatorsReq structure, and it’s not possible to instantiate a structure from an alias to its pointer. Instead an alias to the actual named type is created in order for developers to be able to create new instances of it the usual way:

opts := &InitiatorOptions{}

Yet, as was discussed earlier, it’s not possible to point to the same address with two different named types. It *is* possible, however, to seamlessly create a copy of that data into an instance of the desired type:

pireq := xms.PostInitiatorsReq(*opts)
pires, err := c.api.PostInitiators(&pireq)

The first line above takes the structure to which *opts points, a NewInitiatorOptions, and casts it as a xms.PostInitiatorsReq. Because the two types are different a new xms.PostInitiatorsReq is created. This does represent new memory allocation, but since the majority of this package’s abstracted functions take no parameters or only primitive ones, it’s not a major issue.

Still, how do we know that the above method of casting a value to a new type really does just that? That the data survives intact? Well, here’s a short and sweet go program to prove it:

package main

import "fmt"

type ExternalSubType struct {
  NickName string
}

type ExternalType struct {
  Name string
  Data *ExternalSubType
}

type MyTypeAlias ExternalType
type MyTypePtrAlias *ExternalType

func main() {
  v0 := ExternalType{"Andrew", &ExternalSubType{"Idjit"}}
  v1 := &v0
  v2 := MyTypeAlias(v0)
  v3 := MyTypePtrAlias(&v0)
  v4 := MyTypePtrAlias(v1)

  fmt.Printf("v0 %-11[1]p %-20[1]T %[1]v\n", &v0)
  fmt.Printf("v1 %-11[1]p %-20[1]T %[1]v\n", v1)
  fmt.Printf("v2 %-11[1]p %-20[1]T %[1]v\n", &v2)
  fmt.Printf("v3 %-11[1]p %-20[1]T %[1]v\n", v3)
  fmt.Printf("v4 %-11[1]p %-20[1]T %[1]v\n", v4)
  
  // Change the value of the Name field for the
  // structure. All variables that refer to this
  // instance will reflect this change.
  v0.Name = "werdnA"
  fmt.Printf("\nv0.Name = \"%s\"\n\n", v0.Name)
  
  fmt.Printf("v0 %-11[1]p %-20[1]T %[1]v\n", &v0)
  fmt.Printf("v1 %-11[1]p %-20[1]T %[1]v\n", v1)
  fmt.Printf("v2 %-11[1]p %-20[1]T %[1]v\n", &v2)
  fmt.Printf("v3 %-11[1]p %-20[1]T %[1]v\n", v3)
  fmt.Printf("v4 %-11[1]p %-20[1]T %[1]v\n", v4)
}

Running the above program will produce output similar to the following:

v0 0x104382e0  *main.ExternalType   &{Andrew 0x1040a130}
v1 0x104382e0  *main.ExternalType   &{Andrew 0x1040a130}
v2 0x104382f0  *main.MyTypeAlias    &{Andrew 0x1040a130}
v3 0x104382e0  main.MyTypePtrAlias  &{Andrew 0x1040a130}
v4 0x104382e0  main.MyTypePtrAlias  &{Andrew 0x1040a130}

v0.Name = "werdnA"

v0 0x104382e0  *main.ExternalType   &{werdnA 0x1040a130}
v1 0x104382e0  *main.ExternalType   &{werdnA 0x1040a130}
v2 0x104382f0  *main.MyTypeAlias    &{Andrew 0x1040a130}
v3 0x104382e0  main.MyTypePtrAlias  &{werdnA 0x1040a130}
v4 0x104382e0  main.MyTypePtrAlias  &{werdnA 0x1040a130}

v0, v1, v2, v3, and v4 are all equal with regards to their fields’ data, but v2 is not the same as the others. Let’s look at the variables’ types:

  • v0 –  ExternalType
  • v1*ExternalType
  • v2 –  MyTypeAlias
  • v3 –  MyTypePtrAlias
  • v4 –  MyTypePtrAlias

v2 is the only variable with an underlying type that is not ExternalType. v1 is just a pointer to a ExternalType value, thus the underlying type is ExternalType.

v3 and v4 may have a type of MyTypePtrAlias, but that type is actually an alias for *ExternalType, which has an underlying type of ExternalType.

That is why the above output shows that while all of the variables, including v2, have a value of &{Andrew <0x&ExternalSubType>}, only the variables v0, v1, v3, and v4 share the same memory address.

The point of this demonstration is to show that it’s possible in golang to alias types and use those aliases to refer to the same memory addresses of other types in a type-safe manner. However, the aliases should be defined as to the pointer of another type, not to the concrete type itself.

storage

Embedding Javascript in JIRA with Custom Fields

Did you know you can embed Javascript in JIRA via custom fields? You may wonder, what’s the point? To you good sir, or madame, I say that the point is 75% because you can, and 75% because it enables customization of the JIRA interface without having to resort to writing JIRA plug-ins. And because JIRA ships with jQuery 1.8.3 in JIRA 6, it makes writing complex Javascript a breeze.

Just recently I needed to customize the appearance of the built-in Environment field in the Details section when viewing an issue. To reduce configuration complexity Atlassian actually embeds the look and feel of some of the default fields inside velocity templates which are themselves includes as resources in JIRA JAR files.

In other words, you cannot modify these settings through normal means.

So I created a custom field and added it to my default view and field configuration. I called the custom field "JS Custom Field". It is the one field into which I’ll embed all of my Javascript. I decided to use a single-field approach to centralize my changes as well as preserving the descriptions of the fields I may want to modify with the Javascript. Because that’s where you actually embed your Javascript — in the descriptions of the fields. Crazy, right?

Right out of the gate I wanted to accomplish two things:

  • Customize the look and feel of the Environment field in the Details section when viewing an issue.
  • Transform a multi-select custom field I created into an autocomplete text box.

Oh, and while the custom field that will contain my Javascript has to be added to the DOM in order for its Javascript to be loaded, I do not actually ever want any users seeing it. So the Javascript must also ensure that the custom field is never seen, and can never be made to be seen.

And thus this little bit of Javascript was born. It:

  • Customize the look and feel of the Environment field in the Details section when viewing an issue.
  • Transforms a multi-select custom field into an autocomplete box.
  • Prevents users from ever seeing or making visible the custom field that contains the Javascript.

Please note that my custom field IDs will be different than yours. You should use Chrome’s Developer Tools (or some other browser’s) to determine the IDs of your custom fields.

<script type="text/javascript">
    
    /**
     * The ID of the custom field that contains this javascript and that will
     * be hidden from the user.
     */
    JAVASCRIPT_FIELD_ID = '10313';

    /**
     * The common usage pattern for the javascript field ID.
     */ 
    JAVASCRIPT_CUSTOM_FIELD_STR = 'customfield_' + JAVASCRIPT_FIELD_ID;

    /**
     * The Environment val selector.
     */
    ENV_VAL_SEL = '#environment-val';

    /**
     * The Environment field selector.
     */
    ENV_FIELD_SEL = '#field-environment';

    /**
     * The selector to use to get the Environment field if it's been detwixed.
     */
    ENV_FIELD_DETWIXED_SEL = '#field-environment.user-content-block';

    /**
     * The vC Ops Version custom field ID.
     */
    VC_OPS_VER_FIELD_ID = '10305';

    /**
     * The vC Ops Version field selector.
     */
    VC_OPS_VER_FIELD_SEL = '#customfield_' + VC_OPS_VER_FIELD_ID;

    /**
     * The selector to get the "Edit Issue" button.
     */
    EDIT_ISSUE_BUTTON_SEL = '#edit-issue';

    /**
     * The selector to get the 'All' or 'Custom' links that change the view of 
     * the field picker popup.
     */
    FIELD_PICKER_VIEW_SEL = '#inline-dialog-field_picker_popup a[class*="configurable"]';

    /**
     * The selector to get the control in the field picker popup that toggles
     * the visibility of the custom field in the editor dialog.
     */
    FIELD_PICKER_TOGGLE_SEL = '[data-field-id="' + JAVASCRIPT_CUSTOM_FIELD_STR + '"]';

    /**
     * The selector to get the custom field on the editor dialog when the 
     * 'Custom' view is enabled.
     */
    EDIT_FIELD_CUSTOM_VIEW_SEL = '#qf-field-' + JAVASCRIPT_CUSTOM_FIELD_STR;

    /**
     * The selector to get the custom field on the editor dialog when the 
     * 'All' view is enabled.
     */
    EDIT_FIELD_ALL_VIEW_SEL = 'div.field-group:has(textarea#' + JAVASCRIPT_CUSTOM_FIELD_STR + ')';

    /**
     * The selector to get the editor popup.
     */
    EDIT_ISSUE_DIALOG_SEL = '#edit-issue-dialog';

    /**
     * The selector to get the "Configure Fields" button on the edit dialog.
     */
    CONFIG_FIELDS_BUTTON_SEL = '#qf-field-picker-trigger';

    /**
     * The selector to get the field picker popup.
     */
    FIELD_PICKER_POPUP_SEL = '#inline-dialog-field_picker_popup';

    /**
     * The field that stores the interval ID set when listening for the field
     * picker popup.
     */ 
    var fieldPickerPopupIntervalId = -1;

    /**
     * Converts a multiselect control into a multi-select text field with 
     * autocomplete capabilities.
     *
     * multiSelector The multiselect control's selector.
     */
    function convertMultiIntoAutoCompleteTextArea(multiSelector)
    {
        if ($(multiSelector+ "-textarea").length) return;
        new AJS.MultiSelect({
            element: $(multiSelector),
            itemAttrDisplayed: "label",
            errorMessage: AJS.params.multiselectComponentsError
        });
    }

    function removeMultiSelectNoneOption(multiSelector)
    {
        $(multiSelector + " option[value='-1']").remove();
    }

    /**
     * Removes the twixification of the Environment field on the issue's view
     * page inside the Details section. This makes the Environment field a 
     * normal text area, such as the Description field.
     */
    function deTwixifyEnvField()
    {
        // Modify the environment field in the issue's details view.
        div_env_val = $(ENV_FIELD_SEL);
        if (!div_env_val.length) return;

        div_env_val.attr('class', 
            'field-ignore-highlight editable-field inactive');

        div_env_field = $(ENV_FIELD_SEL);
        div_env_field.attr('class', 'user-content-block');
        div_env_field.find('div:first-child').removeClass();

        div_env_val.find('a:first-child').remove();
    }

    /**
     * Removes the custom field from the editor dialog.
     */
    function removeEditField()
    {
        target = $(EDIT_FIELD_CUSTOM_VIEW_SEL);
        if (!target.length) target = $(EDIT_FIELD_ALL_VIEW_SEL);
        target.remove();
    }

    /**
     * Removes the anchor that allows a user to enable the custom field on the
     * editor dialog.
     */
    function removeFieldPicker()
    {
        $(FIELD_PICKER_TOGGLE_SEL).remove();
    }

    function listenForEditIssueDialog()
    {
        // We only care about when the edit issue dialog is available.
        if (!$(EDIT_ISSUE_DIALOG_SEL).length) return true;
        
        // Get rid of this listener as quickly as possible, it's expensive.
        JIRA.unbind(JIRA.Events.NEW_CONTENT_ADDED, listenForEditIssueDialog);

        // Hide the javascript fields from the editor.
        removeEditField();

        // If the user decides to look at the field picker, don't show
        // the option to enable the javascript fields.
        $(CONFIG_FIELDS_BUTTON_SEL).one('click', registerFieldPickerPopupListener);
    }

    function registerFieldPickerPopupListener()
    {
        // Don't register it if it's currently registered.
        if (fieldPickerPopupIntervalId > -1) return;

        fieldPickerPopupIntervalId = 
            window.setInterval('listenForFieldPickerPopup()', 50);
    }

    function listenForFieldPickerPopup()
    {
        // We only care about when the field picker popup is available.
        if (!$(FIELD_PICKER_POPUP_SEL).length) return true;

        fieldPickerPopupIntervalId = -1;
        window.clearInterval(fieldPickerPopupIntervalId);
        
        // Get rid of this listener as quickly as possible, it's expensive.
        JIRA.unbind(JIRA.Events.NEW_CONTENT_ADDED, listenForFieldPickerPopup);

        // Hide the field picker links from the user.
        removeFieldPicker();

        // Hide the editor fields again as well in case the field picker
        // view changed. This can result in the editor fields being added
        // back to the editor popup.
        removeEditField();

        // If the user decides to change the field picker's view then we need
        // to prepare to remove them once again.
        $(FIELD_PICKER_VIEW_SEL).one('click', registerFieldPickerPopupListener);
    }

    function registerEditIssueDialogListener()
    {
        // This is kind of expensive, but necessary. That's why we're limiting
        // it to when the user clicks the Edit button instead of leaving it
        // enabled at all times.
        JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, listenForEditIssueDialog);
    }

    function listenForEnvironmentField()
    {
        if ($(ENV_FIELD_DETWIXED_SEL).length) return true;
        deTwixifyEnvField();
    }

    function listenForVcOpsVersionField()
    {
        if (!$(VC_OPS_VER_FIELD_SEL).length) return true;
        removeMultiSelectNoneOption(VC_OPS_VER_FIELD_SEL);
        convertMultiIntoAutoCompleteTextArea(VC_OPS_VER_FIELD_SEL);
    }

    function processVcOpsVersionField()
    {
        // Convert the vC Ops Version select box into an autocomplete text area.
        AJS.toInit(listenForVcOpsVersionField);
        JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, listenForVcOpsVersionField);
    }

    function processEnvironmentField()
    {
        // Silly Atlassian, twix are for kids!
        AJS.toInit(listenForEnvironmentField);
        JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, listenForEnvironmentField);
    }

    // When the 'Edit Issue' button is clicked we want to listen for the
    // appearance of the edit dialog.
    $(EDIT_ISSUE_BUTTON_SEL).click(registerEditIssueDialogListener);

    // Process the fields.
    processVcOpsVersionField();
    processEnvironmentField();

</script>
software development

Merging Hierarchical Paths in Java

I’ve been doing heavy refactoring over the last 48 hours to cope with epic VMware fail. One part of that has been the necessity to do something pretty tricky, and as it turns out, Apache Commons has a massive shortcut I thought I’d share with the rest of you. Imagine there are multiple tree branch paths and let KX equal the terminating value:

A.B.C.D.K1
A.B.K2
D.K3

I needed to merge these paths so that I ended up with one hierarchy:

A
|
|--B
    |-- K2
    |
    |--C
        |
        |--D
            |--K1
            |--K3

I knew how to do this merge with linear performance, but I was trying to come up with a solution that was more optimal. It turns out Apache already has one, and it’s called Hierarchical Configuration. Basically you just do the following:

import org.apache.commons.configuration.HierarchicalConfiguration;

HierarchicalConfiguration hc = new HierarchicalConfiguration();
hc.addProperty(“A.B.C.D”, “K1”);
hc.addProperty(“A.B”, “K2”);
hc.addProperty(“D”, “K3”);

The object will create new branches if the paths do not yet exist, but if they do, the values will simply be inserted into the appropriate location. This has been a massive timesaver and hopefully you all will find it useful as well.

software development

Preventing Same Version Downgrades with WiX

So I farked up when I was building several MSI packages using the Microsoft Windows XML (WiX) Toolset. I broke a sacred covenant of MSI development and violated the component rules.

This would not normally be a huge concern since the members of the components, the files, are not going to change. However, I did run into an interesting issue. Because my company’s versioning scheme is MAJOR.MINOR.REVISION.BUILD where BUILD is a value auto-incremented by the build server, as far as MSI is concerned 4.0.0.123 is the same version as 4.0.0.187. And because our patch strategy is to simply release a hot-fixed MSI, we have to allow same-version upgrades.

Anyone familiar with MSI upgrades knows the dirty secret that allowing same version upgrades allows same version downgrades. Except these downgrades would result in files disappearing from the filesystem. After much research I determined the issue was because of my stupid decision to group multiple files into single Components. MSI was not applying the standard file versioning rules correctly, and thus during a downgrade the following steps were occurring:

  1. Version X is installed on a system and user attempts to install version X-1.
  2. The installer appears to calculate which Components need to be installed by version and date. It does not include the X-1’s Components because they are already on the system.
  3. The installer removes X’s Components from the file system.
  4. Finally, the installer chooses not to install X-1’s Components because the installer has already calculated that the Components on the filesystem, at version X, were newer.

I found myself in quite a pickle. How do I prevent same version downgrades when the only varying component of the version is the 4th component (which again, MSI ignores)?

Continue reading

apple

Grove-unmade

Last fall I waited in eager anticipation for my Grove Bamboo case. Simply put, Grove makes these wonderful, beautiful, bamboo laser-cut iPhone cases. In fact, they are now making one for the iPad as well. However, I must warn possible purchasers of the Grove cases that even through everyday use, your work-of-art case will likely fail within six months. Here is a picture of the bottom half of mine now:

Broken Grove Bamboo iPhone4 Case
Broken Grove Bamboo iPhone4 Case

One cannot argue that the Grove made iPhone4 case is nothing less than perfect. I have received more comments on how amazing the case is then I can count. However, for $89, a case should not fall apart after taking the bottom half of it off an on on a regular basis just so you can dock it or plug it into a car charger. That said, I may buy another one eventually, if Grove has strengthened their manufacturing process (and glue that holds the black trim to the bamboo). For now I’ll probably invest in a Vapor case that is all aluminum. Should help block the attenuation issues with my phone anyway.