ECMAScript 5 Showing Up In Browsers

A better JavaScript!  ECMAScript 5 was standardized in late 2009 but only recently has it has started showing up in browsers.

It supersedes the 3rd edition, which was ratified in 1999.  This is in IE 9+.

ECMAScript 5 has actually two modes

  • ES5/Default
  • ES5/Strict – Switching to strict mode requires the statement shown to be placed at the start of a file or function. — ‘use strict’;

Future versions are going to be built on top of ES5/Strict and it is recommended that the default version be avoided.

Douglas Crockford goes through what the features are, what can be used right now and what your kids will be able to use: http://channel9.msdn.com/events/MIX/MIX11/EXT13

Some features:

  • Can use trailing commas:  { “trailing”: “comma”, }  or [ “trailing”, “comma”, ]
  • Constants – Infinity, NaN
  • parseInt works!  parseInt(‘08’) === 8
  • JSON.parse(text, reviver) & JSON.stringify(value, replacer, space)
  • Function.prototype.bind added
  • String.prototype.trim added
  • Array.prototype.every added
  • Array.isArray()
  • get/set: These are the getter and setter functions needed for accessor properties
  • Object.keys (much better than for-in)!
  • Object.create
  • Attributes – Value, Writable, Enumerable, Configurable, Get, Set
  • Can limit the object extensibility which can make the object immutable.
  • Strict mode
    • Reserved words: implements, intereface, let, package, private, protected, etc.
    • No more implied glabal variables within functions
    • “this” is not bound to the global object by function form.

Grails vs. Rails vs. Roo!?

There are some great choices out there to choose from when starting a new web development project. Whether you are just doing a simple site or starting your own business. The frameworks, very thankfully, are getting so good. Which one will manage to stand the test of time? The graph below doesn’t mean that the most trend is always going to be the highest one, especially since roo is so fresh and in the past year has been improved dramatically.

ConverterNotFound Using Spring Roo

Using Roo has been really awesome! I would recommend it to anyone starting a web project for the first time that really wanted to use Spring and Java.

Just ran into this error tonight and thought I would blog a little bit about it in case others run into the same error… *plus* I wanted to show a bit of this new Google Guava project!

1
2
3
4
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from 'java.util.Se
t'
to 'java.lang.String'
        at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:181)
        at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:66)

This meant that we need a converter for the show view since the default create from Roo does not take care of the ONE_TO_MANY relationship. Below is the code that was needed in my case.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * A central place to register application Converters and Formatters.
 */

@RooConversionService
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {

    Converter<Set<Property>, String> getPropertySetConverter() {
        return new Converter<Set<Property>, String>() {
            public String convert(Set<Property> properties) {
                return Joiner.on(",").join(properties.toArray());      // 1
            }
        };
    }
  @Override
  protected void installFormatters(FormatterRegistry registry) {
    super.installFormatters(registry);
    registry.addConverter(getPropertySetConverter());
  }
 
}

Notice the line:

1
return Joiner.on(",").join(properties.toArray());      // 1

This is using the Google Guava project to create a String instead of using the old school StringBuilder approach.

Login Switcher

This Greasemonkey plugin works in Chrome and Firefox. The purpose is specific to Windchill but could be programmed to work for any website that you need to continually login to with different users.

—-
Do you frequently switch between different users in Windchill, have you ever just wanted to log out. This script adds a
configurable list of links to the top of the page (specifically for 10.0) allowing you to switch users or logout with a single click.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// ==UserScript==
// @name Windchill User Switcher/Logout
// @description Allow logging out and switching between users with a single click
// @namespace
// @include http://*.<HOST SITE>.com*/Windchill/app/*
// @author Ryan Alberts ralberts@ptc.com
// ==/UserScript==

//--------------------------------------------------------------------------
// Support for Chrome
//--------------------------------------------------------------------------

if (typeof GM_deleteValue == 'undefined') {
    GM_addStyle = function(css) {
        var style = document.createElement('style');
        style.textContent = css;
        document.getElementsByTagName('head')[0].appendChild(style);
    }

    GM_deleteValue = function(name) {
        localStorage.removeItem(name);
    }

    GM_getValue = function(name, defaultValue) {
        var value = localStorage.getItem(name);
        if (!value)
            return defaultValue;
        var type = value[0];
        value = value.substring(1);
        switch (type) {
            case 'b':
                return value == 'true';
            case 'n':
                return Number(value);
            default:
                return value;
        }
    }

    GM_log = function(message) {
        console.log(message);
    }

     GM_registerMenuCommand = function(name, funk) {
    //todo
    }

    GM_setValue = function(name, value) {
        value = (typeof value)[0] + value;
        localStorage.setItem(name, value);
    }
}


//--------------------------------------------------------------------------
// End Support for Chrome
//--------------------------------------------------------------------------


var cachedLogins = null;

function getLogins() {
  var logins;
  if (cachedLogins == null) {
    cachedLogins = new Array();
    if (!GM_getValue('logins')) {
      logins = ["logout:null", "demo:demo", "wcadmin:wcadmin"];
      GM_setValue('logins',logins.join(','));
    }
    logins = GM_getValue('logins','[]').split(',');
    for (var i in logins) {
                        var loginName = logins[i].substring(0,logins[i].indexOf(':'));
      cachedLogins[loginName] = logins[i];
    }
  }

  return cachedLogins;
}

function fillLoginsIn() {
  logins = GM_getValue('logins', '[]').split(',');
    console.log("Decoded logins", logins);
  var str = "";
  for (x in logins) {
    str += logins[x] + "\n";
  }

  document.getElementById('loginEditorTextArea').value = str;
}

function saveLogins() {
  var logins = document.getElementById('loginEditorTextArea').value.split('\n');;
  GM_setValue('logins', logins.join(','));
  cachedLogins = null;
  resetLogins(document.getElementById('loginsList'));
}

function resetLogins(div) {
   div.innerHTML="";
   var logins = getLogins();
   var loc = window.location;
   for (var user in logins) {
      var link = document.createElement('a');
      link.href = loc.protocol + "//" + logins[user] + "@"+ loc.host + ":" + loc.port + loc.pathname + loc.search;
      link.style.marginLeft = "3px";
      link.style.marginRight = "3px";
      link.appendChild(document.createTextNode(user));
      div.appendChild(link);
   }
}

function addLogger () {
   console.log("addLogger()");
   var logger = document.createElement('div');
   logger.style.position = "absolute";
   logger.style.left = "250px";
   logger.style.background = "#FFFFFF";
   logger.style.zIndex = "99999";
   logger.style.fontSize = "10px";
   logger.style.border = "1px solid black";

   var loginsList = document.createElement('span');
   loginsList.id = 'loginsList';
   resetLogins(loginsList);
   logger.appendChild(loginsList);

   var editLink = document.createElement('a');
   editLink.id = "loginEditorLink";
   editLink.href = "#";
   editLink.appendChild(document.createTextNode("Edit Logins"));
   logger.appendChild(editLink);

   document.body.appendChild(logger);

   var loginEditor = document.createElement('div');
   loginEditor.id = 'loginEditor';
   loginEditor.style.position = "absolute";
   loginEditor.style.left = "250px";
   loginEditor.style.top = "20px";
   loginEditor.style.visibility = 'hidden';
   loginEditor.style.background = "#FFFFFF";
   loginEditor.style.zIndex = "99999";
   loginEditor.style.fontSize = "10px";
   loginEditor.style.border = "1px solid black";
   loginEditor.style.width = "200px";
   loginEditor.style.height = "200px";
   loginEditor.style.textAlign = "center";
   var textArea = document.createElement('textarea');
   textArea.id = 'loginEditorTextArea';
   textArea.style.width = "95%";
   textArea.style.height = "170px";
   loginEditor.appendChild(textArea);
   var saveBtn = document.createElement('input');
   saveBtn.type = 'button';
   saveBtn.id = 'loginEditorSaveButton';
   saveBtn.value = 'Save the Logins';
   loginEditor.appendChild(saveBtn);

   document.body.appendChild(loginEditor);

   unsafeWindow.document.getElementById('loginEditorLink').onclick = function () {
  fillLoginsIn();
  document.getElementById('loginEditor').style.visibility = 'visible';
   };
   unsafeWindow.document.getElementById('loginEditorSaveButton').onclick = function () {
  document.getElementById('loginEditor').style.visibility = 'hidden';
  saveLogins();
   };

}

window.addEventListener('load', addLogger, true);
addLogger();

Error starting Sun’s native2ascii: In Grails

If you are seeing this error what I would recommend is to add the JDK jars to your eclipse “Installed JRE”. This will then use the JDK for developing Grails rather then using the standard JRE.

To change to using the JDK in Eclipse

  • Go to Window -> Preferences.
  • Select Java -> Installed JREs
  • Click “Add” button
  • Go through the wizard to add your downloaded JDK
  • Select your newly added JDK back at the main window

Then you should be good to go!

Hudson Modify Environment Variables Properties

Myself and another have been trying to figure out how to modify the environment variables programmaticly using groovy in Hudson. After a little of tinkering we were able to do it.

1
2
3
4
5
6
7
def hudson = hudson.model.Hudson.instance
def globalProps = hudson.globalNodeProperties
def props = globalProps.getAll(hudson.slaves.EnvironmentVariablesNodeProperty.class)
for (prop in props) {
    prop.envVars.put("TEST_ENV", "WORKS")
}
hudson.save() //This is needed in order to persist the change

Then if you want to expand this for the slaves:

1
2
3
4
5
6
7
8
9
10
11
12
def slaves = hudson.model.Hudson.instance.getNodes()
for (slave in slaves) {
    println "-----------------------"
    println slave.name
    println "-----------------------"

    def props = slave.nodeProperties.getAll(hudson.slaves.EnvironmentVariablesNodeProperty.class)
    for (prop in props) {
        prop.envVars.put("TEST_ENV", "WORKS")
    }
    //SAVE
}

JSON Output In Grails Controller

I wanted to be able to output an object with all the domain properties. This is obviously very easy in Grails:

1
MyObject as JSON

What if you wanted to be able to add to the MyObject? There are a bunch of ways that Grails provides in order to allow you to let you do this. Unfortunately, I didn’t know all of them at first so I had to do a bit of research.

Since I just wanted to add a simple property to render I added a transient value to my object:

1
static transients = ["link"]

Then I was able to set this value in the controller by iterating over my objects:

1
myObjectList.each() { it.href = createLink(action:'show', id:it.id).encodeAsHTML() };

Then I could just do the following:

1
render json as JSON

Bang! I have my extra attribute! Depending on what you want to do there might be a better way for your particular situation. As far as the code is concerned, this was a nice short change.

If more is needed I might need to start using the ObjectMarshaller. You can read about that and other ways to work with JSON by visiting the link below. They were probably the best resources I found out there at the moment.

Excellent blog:
Rending JSON Part 2: http://manbuildswebsite.com/2010/02/08/rendering-json-in-grails-part-2-plain-old-groovy-objects-and-domain-objects/

Rending JSON Part 3: http://manbuildswebsite.com/2010/02/15/rendering-json-in-grails-part-3-customise-your-json-with-object-marshallers/

Selenium Localization

Not sure how other corporations are localizing Selenium. It has been an issue with ours. Since the creation of selenium we have not embedded localization into the infrastructure…oops, right?! The funny thing is that it took two of us to implement this simple solution in a couple hours. Once done we sent it off for review by the Automation Testing team. I wanted to let others take a look in case they were in the same situation that we were in.

One key is that we call into our Java server using a JSP would change depending upon how you get your localization entries for selenium.

How we localized Selenium:

•Added a class RBUtil that has methods overloaded method getValue(). As an example it is used in the following way:

1
2
3
4
5
String localizedValue = RBUtil.getValue(KEY_TO_LOCALIZATION)

//There are also signatures for the following:
RBUtil.getValue(key, default)
RBUtil.getValue(key, text insert array)

• The way this works is by calling the server with the resource bundle key. We are using the same Tomcat session as selenium to get the localized value.
• A key is that we are re-using the Tomcat session id so that we are not creating new sessions for each resource bundle (or localized string).
• We also cache the locale, username, etc. In fact, we will probably need to do more caching in the future but the main goal is that this will get us on our feet for having selenium localized.

Implementation Class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package com.ptc.selenium.localization;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import org.apache.log4j.Logger;

import sun.misc.BASE64Encoder;

import com.ptc.selenium.SeleniumLogger;
import com.ptc.selenium.SeleniumSession;
import com.ptc.selenium.SolventSelenium;
import com.ptc.selenium.SolventTestCase;

/**
 * Singleton class that is used to get the localization values from the RBInfo class file entries.
 *
 * This will retrieve values for selenium tests to be able to localize and attribute clicks to rbinfo files.
 *
 */

public class RBUtil {

    private static final Logger log = SeleniumLogger.getLogger(SolventTestCase.class.getName());

    private static RBUtil singleton;

    private final String baseURL;

    private final String username;

    private final String password;

    private final String cookie;

    private final String locale;

    private RBUtil() {
        SolventSelenium selenium = SeleniumSession.get();

        // Cache these values on the instance
        baseURL = selenium.getBaseURL();
        password = selenium.getPassword();
        username = selenium.getUsername();
        cookie = selenium.getCookieByName("JSESSIONID");
        locale = selenium.getEval("selenium.browserbot.getCurrentWindow().getLocale()");
    }

    public static RBUtil getInstance() {
        if (singleton == null) {
            singleton = new RBUtil();
        }
        return singleton;
    }

    /**
     * Will retrieve the resource bundle value given the key in the form of class.key. For example,
     * com.ptc.windchill.enterprise.folder.folderResource.PARTS_VIEW
     *
     * @param key
     *            In the form of CLASS.KEY. For example,
     *            "com.ptc.windchill.enterprise.folder.folderResource.PARTS_VIEW".
     * @return The value returned from the resource bundle lookup. If the resource could not be found empty string is
     *         returned
     */

    public static String getValue(String key) {
        return getValue(key, "");
    }

    /**
     * Will retrieve the resource bundle value given the key in the form of class.key.
     *
     * @param key
     *            In the form of CLASS.KEY. For example,
     *            "com.ptc.windchill.enterprise.folder.folderResource.PARTS_VIEW".
     * @param def
     *            The default value you want in the case that the locale could not be looked up.
     * @return The value returned from the resource bundle lookup.
     */

    public static String getValue(String key, String def) {
        String value = getInstance().getValueFromServer(key);
        return (value == null) ? def : value;
    }

    /**
     * Gets the localized string, unescapes the quot and puts the inserts in.
     *
     * @param key
     *            In the form of CLASS.KEY. For example,
     *            "com.ptc.windchill.enterprise.folder.folderResource.PARTS_VIEW".
     * @param inserts
     *            A list of inserts in order of the numbering in the rbinfo file.
     * @return The final string.
     */

    public static String getValue(String key, List<String> inserts) {
        String value = getInstance().getValueFromServer(key);

        for (int i = 0; i < inserts.size(); i++) {
            value = value.replace("{" + i + "}", inserts.get(i));
        }

        value = value.replaceAll("&quot;", """);

        return value;
    }

    private String getValueFromServer(String key) {
        InputStream ins = null;
        try {
            log.debug("
Using Session Cookie = " + this.cookie);
            URL url = new URL(this.baseURL + "
/netmarkets/jsp/util/getBundleKey.jsp?key=" + key);
            URLConnection conn = url.openConnection();

            conn.setRequestProperty("
Cookie", "JSESSIONID=" + this.cookie);
            conn.setRequestProperty("
Accept-Language", this.locale);
            conn.setRequestProperty("
Authorization", "Basic " + encode(this.username + ":" + this.password));

            String value = null;
            try {
                ins = conn.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
                value = reader.readLine();
                log.debug("
Fetched RbInfo Key=" + key + " Value=" + value);
            } finally {
                if (ins != null) {
                    ins.close();
                }
            }

            return value;
        } catch (Exception e) {
            throw new RuntimeException("
Unable to reach server.", e);
        }
    }

    /**
     * Encode the string for use in the header.
     *
     * @param source
     *            The string you want to encode using base 64 encoding.
     * @return The encoded string.
     */
    public static String encode(String source) {
        BASE64Encoder enc = new sun.misc.BASE64Encoder();
        return (enc.encode(source.getBytes()));
    }

}