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();

Git and Notepad++

Simple but really helpful if you want to use Notepad++ as your default editor when using GIT. The location will depend upon your specific location. I happen to be on Windows 7 x64 bit.

1
2
[core]
    editor = "/C/Program Files (x86)/Notepad++/notepad++.exe" -m

User Experience

At CodeFreeze this year, I went to the User Experience (UX).  They went through things that I have picked up from our UX team but it ended up providing valuable reminders.

One such idea is that on our team we can do user experience testing during requirements, prototype and final stages of development.  Persona’s are necessary, of course, during all stages of development. It is key to understand your customer and how they end up using the system.

Here are a list of things to think about and general tips when going to a customer site to do user testing:

  • Look at the users cube, cheat sheets, etc.
  • Users will often say and do things differently
  • Record their interactions with others
  • How often do they use the software?  all day? occasionally?
  • Who conducts the test?  Business as well as ux team members.  Business / Developers can also be great observers.

Cognitive Walkthrough

  • Take a story and go through it as if you were this person.
  • Getting context around what the person is, what they are doing, etc.
  • Quote: “Take a user to the edge of the cliff and then watch them step off.  Let the user struggle a bit and then come back and ask questions later.”
  • Question you can ask when the user is struggling: Do you see anything else that would help you?

IndexedDB vs. WebSQL

At this point, it sounds like IndexedDB will be more highly adopted then WebSQL.

Mark West did a nice overview. I summarized a his comparison slides below:

WebSQL
• A real, relational db implementation on the client (SQLite)
• Data can be highly structured, and JOIN enables quick, ad-hoc access
• Big conceptual overhead (SQL)
• Not very JavaScripty, browser support is poor, and the spec has been more or less abandoned.

IndexedDB
• Sits between full-on SQL and unstructured key-value pairs in “localStorage”
• Values are stored as structured JavaScript objects, and an indexing system facilitates filtering and lookup.
• Asynchronous, with moderately granular locking
• Joining normalized data is a completely manual process

“The working group is designing this API so it could be wrapped by JavaScript libraries; for instance, there’s plenty of room for a CouchDB-style API on top of our IndexedDB implementation. It would also be very possible to build a SQL-based API on top of IndexedDB (such as WebDatabase).” – Arun Ranganathan, Firefox Developer

Code Comparisons: http://hacks.mozilla.org/2010/06/comparing-indexeddb-and-webdatabase/.

Google Trend of Subversion vs. Git

Interesting to see the trend of using Git keeps increasing.  It has been a battle within our company as to whether we will switch off ClearCase and pick Subversion or Git.  In my mind there is only one choice but it is not me that gets to decide 🙂

Google trend graph of Subversion vs. Git: http://bit.ly/g9Kf1Q

Mysqld Fails to Start

When I ran mysqld I would get:

> 090127 10:00:30 InnoDB: Operating system error number 13 in a file
> operation.
> InnoDB: The error means mysqld does not have the access rights to
> InnoDB: the directory.
> InnoDB: File name ./ibdata1
> InnoDB: File operation call: ‘open’.
> InnoDB: Cannot continue operation.

Finally I looked in /etc/mysql/my.cnf and figured out that my bind-address needed to be updated. So took about an hour to figure that one out. It would be best if I could just leave it to localhost for the bind_address but I can not do that right now.

For others where it is not the bind_address it could be number of other things. I started reading through this thread: http://lists.mysql.com/mysql/216042.  It was very helpful to think through the scenarios.

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
}