If you ever need fibonacci like plannings card.
http://www.pmtoolbox.com/project-management-news/print-your-own-planning-poker-cards.html
Java development
torsdag 11 oktober 2012
fredag 5 oktober 2012
Unit testing for classes using javax.mail.Transport
Found an interesting framework dumbster that simplifies unit testing of mail sending services and APIs. It basically creates a fake mail server that intercepts all SMTP commands.
Here is a simple example of a unit test for the class BasicMailHelper which has a method send that sends an email using SMTP.
After some more testing of dumbster I needed to compensate for dead-lock (i.e. a timing related missing notify) and missing error handling. This is what I ended up with (would be nice to have an updated version of dumpster). I use a CountDownLatch to handle the dead-lock, reflection to get the state of the private field serverSocket to see if the server is correctly initialized (e.g. has not thrown port already in use).
private final static int PORT_MIN = 8000;
private final static int PORT_MAX = 9000;
private static final Logger LOGGER = LoggerFactory.getLogger(MailTest.class);
private void startDumbster()
{
int port = PORT_MIN;
final CountDownLatch[] startedLatch = {null};
while (true)
{
if (port > PORT_MAX)
{
Assert.assertTrue("Unable to find free port", false);
}
// compensate for dead-lock in SimpleSmtpServer.start and missing status flags
startedLatch[0] = new CountDownLatch(1);
new Thread(new Runnable()
{
public void run()
{
server = SimpleSmtpServer.start(port); // might lock the thread, use a timeout
startedLatch[0].countDown();
}
}).start();
startedLatch[0].await(1, TimeUnit.MINUTES);
Field socketField = SimpleSmtpServer.class.getDeclaredField("serverSocket");
socketField.setAccessible(true);
if ((startedLatch[0].getCount() == 0) && !server.isStopped() && (socketField.get(server) != null))
{
LOGGER.info("Successfully started dumpster on port {}", port);
break;
} else
{
port++;
}
}
}
private static final int PORT = 9966;
private static final String HOST = "localhost";
private static final String USER = "aUser";
private static final String FROM = "aFrom";
private static Session session;
@BeforeClass
public static void createSession()
{
// create the mail session
Properties props = new Properties();
props.put("mail.smtp.host", HOST);
props.put("mail.smtp.port", Integer.toString(PORT));
props.put("mail.smtp.user", USER);
props.put("mail.smtp.from", FROM);
props.put("mail.smtp.auth", Boolean.toString(false));
props.put("mail.transport.protocol", "smtp");
session = Session.getInstance(props);
}
@Test
public void testSendTo()
throws MessagingException, IOException
{
// create the fake SMTP dumbster server
SimpleSmtpServer server = SimpleSmtpServer.start(PORT);
// send the email using the class that is to be tested
MailHelper mailHelper = new BasicMailHelper.Builder().setSession(session).build();
String subject = "aSubject";
String body = "aBody";
InternetAddress to = new InternetAddress("test@test.com");
mailHelper.send(subject, body, to);
// make sure all requests are processed by stopping the server
server.stop();
// evaluate the result by examining the emails in dumbster
Assert.assertEquals(1, server.getReceivedEmailSize());
SmtpMessage mail = (SmtpMessage) server.getReceivedEmail().next();
Assert.assertEquals(subject, mail.getHeaderValue("Subject"));
Assert.assertEquals(to.getAddress(), mail.getHeaderValues("To")[0]);
Assert.assertTrue(mail.getBody().contains(body));
}
After some more testing of dumbster I needed to compensate for dead-lock (i.e. a timing related missing notify) and missing error handling. This is what I ended up with (would be nice to have an updated version of dumpster). I use a CountDownLatch to handle the dead-lock, reflection to get the state of the private field serverSocket to see if the server is correctly initialized (e.g. has not thrown port already in use).
private final static int PORT_MIN = 8000;
private final static int PORT_MAX = 9000;
private static final Logger LOGGER = LoggerFactory.getLogger(MailTest.class);
private void startDumbster()
{
int port = PORT_MIN;
final CountDownLatch[] startedLatch = {null};
while (true)
{
if (port > PORT_MAX)
{
Assert.assertTrue("Unable to find free port", false);
}
// compensate for dead-lock in SimpleSmtpServer.start and missing status flags
startedLatch[0] = new CountDownLatch(1);
new Thread(new Runnable()
{
public void run()
{
server = SimpleSmtpServer.start(port); // might lock the thread, use a timeout
startedLatch[0].countDown();
}
}).start();
startedLatch[0].await(1, TimeUnit.MINUTES);
Field socketField = SimpleSmtpServer.class.getDeclaredField("serverSocket");
socketField.setAccessible(true);
if ((startedLatch[0].getCount() == 0) && !server.isStopped() && (socketField.get(server) != null))
{
LOGGER.info("Successfully started dumpster on port {}", port);
break;
} else
{
port++;
}
}
}
torsdag 4 oktober 2012
JMX over HTTP with Jolokia and javascript
Using a combination of jolokia and jquery (with jstree) it is quite easy to create a jconsole like M-Bean handler application in javascript.
The basic idea is to search all M-Beans using jolokia and to create a navigation tree using jstree. When selecting a leaf node the corresponding M-Bean attributes and operations are presented.
<servlet>
<servlet-name>jolokia</servlet-name>
<servlet-class>org.jolokia.http.AgentServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name> jolokia </servlet-name>
<url-pattern>/jolokia/*</url-pattern>
</servlet-mapping>
By using the javascipt API for jolokia to search for M-Beans it is possible to present a jconsole like tree structure using jstree.
NOTE: the keyorder:"constructionTime" is a patch to jolokia making it possible to get the M-Bean keys in construction order instead of cannonical. This feature makes the tree look like jconsole but is currently not available in jolokia (just omit it).
Disclaimer: I'm obviously not a javascript developer, sorry for the coding style :)
<div id="nav" class="jstree"></div>
<script type="text/javascript">
function extractDomainMap(mbeans) {
var domains = {};
for (var i = 0; i < mbeans.length; i++) {
var mbean = mbeans[i];
var domainEnd = mbean.indexOf(":");
var domainName = mbean.substring(0, domainEnd);
var domain = domains[domainName];
if (domain == null) {
domain = {"name": domainName, "entries": {}};
domains[domainName] = domain;
}
var container = domain.entries;
var tokens = mbean.substring(domainEnd + 1).split(",");
for (var ii = 0; ii < tokens.length; ii++) {
var token = tokens[ii];
var typeEnd = token.indexOf("=");
var name = token.substring(typeEnd + 1);
var item = container[name];
if (item == null) {
item = {"name": name, "entries": []};
container[name] = item;
}
if (ii == (tokens.length - 1)) {
item.mbean = mbean;
}
container = item.entries;
}
}
return domains;
}
function createJsTreeDataRecursive(entries, children) {
var i = 0;
for (var itemName in entries) {
if (entries.hasOwnProperty(itemName)) {
var item = entries[itemName];
var node = {"data": item.name, "children": []};
if (item.mbean != null) {
node.metadata = {"mbean": item.mbean};
}
children[i] = node;
createJsTreeDataRecursive(item.entries, node.children);
i++;
}
}
}
function createJsTreeData(domains) {
var data = {"data" : []};
var i = 0;
for (var domainName in domains) {
if (domains.hasOwnProperty(domainName)) {
var domain = domains[domainName];
var node = {"data": domain.name, "children": []};
data.data[i] = node;
createJsTreeDataRecursive(domain.entries, node.children);
i++;
}
}
return data;
}
function searchAll() {
var response = new Jolokia({"url": "jolokia"}).request(
{type: "search", mbean:"*:*", keyorder:"constructionTime"},
{method: "post"});
response.value.sort();
return createJsTreeData(extractDomainMap(response.value));
}
var data = searchAll();
$("#nav")
.jstree ({
"json_data" : data,
"themes" : {
"theme" : "classic",
"dots" : false
},
"core" : {
"animation" : 50
},
"plugins" : ["themes", "classic", "json_data","ui"]
})
.bind("select_node.jstree", function (event, data) {
var mbean = data.rslt.obj.data("mbean");
if (mbean != null) {
listMbean(mbean);
}
});
</script>
The listMBean function is invoked when an leaf node is selected. It is quite easy to create a jquery tablesorter of all the attributes, operations and notifications using for example the cool javascript template framework handlebars. Here is the code that creates a table of the M-Bean attributes
<div id="attributes"></div>
<script id="attributes-template" type="text/x-handlebars-template">
<table id="attributes-table" class="tablesorter">
<thead>
<th>Name</th>
<th>Value</th>
</thead>
<tbody>
{{#attributes}}
<tr>
<td>{{name}}</td>
<td id='{{name}}'>
{{#if value.rw}}
<input class="attr-input" id='attr-input-{{name}}' name='{{name}}' type='text' value=''>
<input class="attr-submit" id='attr-submit-{{name}}' name='Save' type='submit' value='Save' onclick="updateAttribute('{{name}}')">
{{/if}}
</td>
</tr>
{{/attributes}}
</tbody>
</table>
</script>
<script type="text/javascript">
var currentMbean;
function listMbean(mbean) {
var path = mbean.replace(new RegExp("/", 'g'), "!/");
path = path.replace(":", "/");
var meta = new Jolokia({"url": "jolokia"}).request(
{"type": "list", "path": path},
{method: "post"});
currentMbean = mbean;
currentMeta = meta;
$("#mbeanInfo").html("<h2 id='mbeanName'>" + mbean + "</h2><p>" + meta.value.desc + "</p>");
var attributes = { 'attributes' : [] };
for (var attr in meta.value.attr) {
if (meta.value.attr.hasOwnProperty(attr)) {
attributes['attributes'].push({
'name' : attr,
'value' : meta.value.attr[attr]
});
}
}
var source = $("#attributes-template").html();
var template = Handlebars.compile(source);
$("#attributes").html(template(attributes));
$("#attributes-table").tablesorter({widgets: ['zebra']});
// inject the values
var values = new Jolokia({"url": "jolokia"}).request(
{"type": "read", "mbean": mbean},
{method: "post"});
for (var attr in values.value) {
if (values.value.hasOwnProperty(attr)) {
var value = values.value[attr];
if (meta.value.attr[attr].rw) {
$("#attr-input-" + attr).val(value);
} else {
$("#" + attr).html(value);
}
}
}
}
function updateAttribute(name) {
var mbean = currentMbean;
var value = $("#attr-input-" + name).val();
new Jolokia({"url": "jolokia"}).request(
{"type": "write", "mbean": mbean, "attribute": name, "value": value},
{method: "post"});
listMbean(mbean);
}
</script>
Similar tables can be created for the M-Bean operations and notifications.
The basic idea is to search all M-Beans using jolokia and to create a navigation tree using jstree. When selecting a leaf node the corresponding M-Bean attributes and operations are presented.
Implementation
The AgenServlet from jolokia was added to a servletcontainer (tomcat) in web.xml<servlet>
<servlet-name>jolokia</servlet-name>
<servlet-class>org.jolokia.http.AgentServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name> jolokia </servlet-name>
<url-pattern>/jolokia/*</url-pattern>
</servlet-mapping>
By using the javascipt API for jolokia to search for M-Beans it is possible to present a jconsole like tree structure using jstree.
NOTE: the keyorder:"constructionTime" is a patch to jolokia making it possible to get the M-Bean keys in construction order instead of cannonical. This feature makes the tree look like jconsole but is currently not available in jolokia (just omit it).
Disclaimer: I'm obviously not a javascript developer, sorry for the coding style :)
<div id="nav" class="jstree"></div>
<script type="text/javascript">
function extractDomainMap(mbeans) {
var domains = {};
for (var i = 0; i < mbeans.length; i++) {
var mbean = mbeans[i];
var domainEnd = mbean.indexOf(":");
var domainName = mbean.substring(0, domainEnd);
var domain = domains[domainName];
if (domain == null) {
domain = {"name": domainName, "entries": {}};
domains[domainName] = domain;
}
var container = domain.entries;
var tokens = mbean.substring(domainEnd + 1).split(",");
for (var ii = 0; ii < tokens.length; ii++) {
var token = tokens[ii];
var typeEnd = token.indexOf("=");
var name = token.substring(typeEnd + 1);
var item = container[name];
if (item == null) {
item = {"name": name, "entries": []};
container[name] = item;
}
if (ii == (tokens.length - 1)) {
item.mbean = mbean;
}
container = item.entries;
}
}
return domains;
}
function createJsTreeDataRecursive(entries, children) {
var i = 0;
for (var itemName in entries) {
if (entries.hasOwnProperty(itemName)) {
var item = entries[itemName];
var node = {"data": item.name, "children": []};
if (item.mbean != null) {
node.metadata = {"mbean": item.mbean};
}
children[i] = node;
createJsTreeDataRecursive(item.entries, node.children);
i++;
}
}
}
function createJsTreeData(domains) {
var data = {"data" : []};
var i = 0;
for (var domainName in domains) {
if (domains.hasOwnProperty(domainName)) {
var domain = domains[domainName];
var node = {"data": domain.name, "children": []};
data.data[i] = node;
createJsTreeDataRecursive(domain.entries, node.children);
i++;
}
}
return data;
}
function searchAll() {
var response = new Jolokia({"url": "jolokia"}).request(
{type: "search", mbean:"*:*", keyorder:"constructionTime"},
{method: "post"});
response.value.sort();
return createJsTreeData(extractDomainMap(response.value));
}
var data = searchAll();
$("#nav")
.jstree ({
"json_data" : data,
"themes" : {
"theme" : "classic",
"dots" : false
},
"core" : {
"animation" : 50
},
"plugins" : ["themes", "classic", "json_data","ui"]
})
.bind("select_node.jstree", function (event, data) {
var mbean = data.rslt.obj.data("mbean");
if (mbean != null) {
listMbean(mbean);
}
});
</script>
The listMBean function is invoked when an leaf node is selected. It is quite easy to create a jquery tablesorter of all the attributes, operations and notifications using for example the cool javascript template framework handlebars. Here is the code that creates a table of the M-Bean attributes
<div id="attributes"></div>
<script id="attributes-template" type="text/x-handlebars-template">
<table id="attributes-table" class="tablesorter">
<thead>
<th>Name</th>
<th>Value</th>
</thead>
<tbody>
{{#attributes}}
<tr>
<td>{{name}}</td>
<td id='{{name}}'>
{{#if value.rw}}
<input class="attr-input" id='attr-input-{{name}}' name='{{name}}' type='text' value=''>
<input class="attr-submit" id='attr-submit-{{name}}' name='Save' type='submit' value='Save' onclick="updateAttribute('{{name}}')">
{{/if}}
</td>
</tr>
{{/attributes}}
</tbody>
</table>
</script>
<script type="text/javascript">
var currentMbean;
function listMbean(mbean) {
var path = mbean.replace(new RegExp("/", 'g'), "!/");
path = path.replace(":", "/");
var meta = new Jolokia({"url": "jolokia"}).request(
{"type": "list", "path": path},
{method: "post"});
currentMbean = mbean;
currentMeta = meta;
$("#mbeanInfo").html("<h2 id='mbeanName'>" + mbean + "</h2><p>" + meta.value.desc + "</p>");
var attributes = { 'attributes' : [] };
for (var attr in meta.value.attr) {
if (meta.value.attr.hasOwnProperty(attr)) {
attributes['attributes'].push({
'name' : attr,
'value' : meta.value.attr[attr]
});
}
}
var source = $("#attributes-template").html();
var template = Handlebars.compile(source);
$("#attributes").html(template(attributes));
$("#attributes-table").tablesorter({widgets: ['zebra']});
// inject the values
var values = new Jolokia({"url": "jolokia"}).request(
{"type": "read", "mbean": mbean},
{method: "post"});
for (var attr in values.value) {
if (values.value.hasOwnProperty(attr)) {
var value = values.value[attr];
if (meta.value.attr[attr].rw) {
$("#attr-input-" + attr).val(value);
} else {
$("#" + attr).html(value);
}
}
}
}
function updateAttribute(name) {
var mbean = currentMbean;
var value = $("#attr-input-" + name).val();
new Jolokia({"url": "jolokia"}).request(
{"type": "write", "mbean": mbean, "attribute": name, "value": value},
{method: "post"});
listMbean(mbean);
}
</script>
Similar tables can be created for the M-Bean operations and notifications.
Etiketter:
handlebars,
javascipt,
jconsole,
jmx,
jolokia,
jstree,
mbean,
tablesorter
torsdag 20 september 2012
onsdag 29 augusti 2012
tisdag 28 augusti 2012
Subversion: What svn revision was this branch created in?
Open a shell in the project root
>> svn log --stop-on-copy .
Returns a list of revisions. The first will be at the end of the list
Staging: initial logging
------------------------------------------------------------------------
r72664 | mikael | 2012-05-28 09:44:57 +0200 (Mon, 28 May 2012) | 1 line
Prenumerera på:
Inlägg (Atom)