Tuesday, July 28, 2009

updating more than one div with grails and ajax

sometimes it happens that you have to update several div's on one webpage, when you click on a form or a button.

Now you can either reload the complete page, which can take a while with a lot of data or just queue remote functions in a java script function like this


function addStandard() {

if (YAHOO.example.container.addstandard) {
YAHOO.example.container.addstandard.hide();
}

//get our element values
var conc = document.getElementsByName('qs_concentration')[0].value;
var pattern = document.getElementsByName('qs_pattern')[0].value;

//calls the remote functions
${remoteFunction(
controller: 'management',
action: 'addStandard_ajax',
params: '\'conc=\' + escape(conc) + \'&pattern=\' + escape(pattern)',
update: 'registered_standards')}

${remoteFunction(
controller: 'messenger',
action: 'showMessage',
params: '\'message=you should recalculate the database now!\'',
update: 'messenger')}
}

javascripts and maps

since java script contains no Maps, well use an object instead with dynamic properties.


var test = new Object();
test['tada'] = 'yada';

alert(test['yada']);
alert(test.yada);

distinct or group by, what's more expensive

today I was asking my self about the best approach for a performance critical application.

Distinct or group by '*'



select name from table group by name



or



select distinct name from table



after all they both return the exact same result.

Some explain statements later it turned out that the group by clause is 18% faster than the distinct statement.

Monday, July 27, 2009

sometimes it's simpler to use plain ajax...

today I was struggling for a bit with generating the update part of a remote function on the fly and after a couple of minutes. I started thinking, do I have to really use this tag? Why don't I use just plain Ajax, since grails will generate the same code anyway.

Solution:


<div class="center">
<img name="${bin.getId()}" src="${createLinkTo(dir: 'images', file: 'remove.gif')}" alt="zoom" onclick="new Ajax.Updater

('bin-container-${db}', '/quality-war/management/remove_bin_ajax', {asynchronous:true,evalScripts:true,parameters


:'bin=${bin.getId()}&database=${db}'});"/>
</div>


this also shows how to submit several parameters at once in plain ajax style.

lesson of the day...

and god said you shall no duplicate div ids,
cause if you do,
you shall be send to the hell of css,
constant torture and misfortune will await you,

so do not duplicate your id's!

problem:

updating a you tab with some dynamic generated content, which generate the same div id over and over again...

Annoying maven bug

well tonight I got an interesting error message from my bamboo build server


[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Error installing artifact's metadata: Error installing metadata: Error updating group repository metadata

input contained no data
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch


without actually doing any changes at the pom.xml. The problem turned out that the repository was corrupt.

So all I had todo was to delete my project from the local build repository.

Thursday, July 23, 2009

Grails - using several arguments with a remote function

in grails it's a bit tricky to use a remote function and pass more than one parameter, since it does not follow the map approach like the usual approach

After a bit of asking Dr Google I finally found an approach which worked


var conc = document.getElementsByName('qs_concentration')[0].value;
var pattern = document.getElementsByName('qs_pattern')[0].value;

${remoteFunction(
controller: 'management',
action: 'addStandard_ajax',
params: '\'conc=\' + escape(conc) + \'&pattern=\' + escape(pattern)',
update: 'registered_standards')}


now I just wish there would be an easier approach

grails remote function on image or checkbox

Today I was in the situation that I wanted to call a remote function on an image to emulate a button, to delete an object with a specific id.

It's simple enough,



<img name="${standard.getPosition()}" src="${createLinkTo(dir: 'images', file: 'remove.gif')}" alt="zoom" onclick="${remoteFunction(
controller: 'management',
action: 'removeStandard_ajax',
params: '\'standard=\' + + escape(this.name)',
update: 'registered_standards')}"/>


To make live easy we just save the id in the name field of the image, but you can also save it somewhere else, depending on your requirements.

Now another thing you can do is todo the same thing for checkfields.


<input type="checkbox" id="cacheSetting" name="cacheSetting" onchange="${remoteFunction(
controller: 'management',
action: 'updateCache_ajax',
params: '\'cacheSetting=\' + escape(this.checked)',
update: 'general_config')}"/>


If you want to preserve the check state you can do this with this


<input type="checkbox" id="cacheSetting" name="cacheSetting" <%if(cacheSetting) println "checked"%> onchange="${remoteFunction(
controller: 'management',
action: 'updateCache_ajax',
params: '\'cacheSetting=\' + escape(this.checked)',
update: 'general_config')}"/>


and an example controller function could look like this



def updateCache_ajax = {
assert params.cacheSetting != null, "you need to provide \"cacheSetting\" in the params object"
BinBaseQualityConnector.getQualityConfigService().setCaching(Boolean.parseBoolean(params.cacheSetting))

//define our model
def model = new HashMap()

//build the model
model.cacheSetting = BinBaseQualityConnector.getQualityConfigService().isCaching()

render(template: "general", model: model)
}

Monday, July 20, 2009

scala in grails

I know it's evil and I know you don't want todo this, but what the heck. This plugin allows you to compile and use scala code in grails.

I don't know why I would want to use scala right now in this, but I'm sure I find out a reason or two :)

Thursday, July 16, 2009

YUI and Prototype - updating an element

Well currently I'm working a lot with YUI and specially tabs. So one of the first things I wanted todo is to select a tab and this selections should do two things
  • show the content of the tab, happens by default
  • update a div on the page somewhere with some content related to the tab content
so how do you do this?

Quite simple.



<script type="text/javascript">
var myTabs = new YAHOO.widget.TabView('demo');
var tab0 = myTabs.getTab(0);

function handleClick(e,tab) {
${"div_to_update"}.update("I just clicked on my tab!");
}

tab0.addListener('click', handleClick,tab0);
</script>



and as you can see we use the 'update' function from the prototype library.

Wednesday, July 15, 2009

Grails - ajax driven select and saving the selection

A couple of days ago I discovered the need of ajax populated forms to improve the navigation of the current website I'm working on.

And some nice person wrote a great tutorial about this here, but there was one thing missing. I wanted to render some elements on the same page and keep the selection stored in the text field.

The default approach is the following grails tag:


<g:select id="machine" name="machine" value = "${machine}"/>


but since this field is populated by an ajax function, the value can never be set,since the population happens after the page is loaed, so what can we do?

We enhance our little bit of java script and define the selection in there


//update the machine tag with the received values
function updateMachine(e) {
// The response comes back as a bunch-o-JSON
var machines = eval("(" + e.responseText + ")"); // evaluate JSON


if (machines) {
var rselect = document.getElementById('machine');

// Clear all previous options
var l = rselect.length;

while (l > 0) {
l--;
rselect.remove(l) ;
}

// Rebuild the select
for (var i = 0; i < machine =" machines[i];" opt =" document.createElement('option');" text =" machine;" machine = <%=machine%>'){
opt.selected = machine;
}
//just add another field and work with the default selection
else{
opt.value = machine;
}
try {
rselect.add(opt, null); // standards compliant; doesn't work in IE
}
catch(ex) {
rselect.add(opt); // IE only
}
}
}
}

//initilaize the variables on page load
var zselect = document.getElementById('database') ;
var zopt = zselect.options[zselect.selectedIndex]
${remoteFunction(controller: 'query', action: 'listmachines_ajax', params: '\'database=\' + zopt.value', onComplete: 'updateMachine(e)')}



we basically assume that our controller inject a variable with the name 'machine' on submitting the form. A controller closure could look like this:


def showTimeLine_ajax = {

assert params.database != null, "you need to provide the database in the params object"
assert params.machine != null, "you need to provide the machine in the params object"

def model = new HashMap()

//build our model, implementation not shown because of copyright
//model = buildModel(params.database,params.machine)

model.database = params.database
model.machine = params.machine

return model
}



and our form is simple enough


<g:form name="select_database_machine_form" method="post">
<g:select id="database" name="database" from="${dbs}" value="${database}"

onchange="${remoteFunction(
controller:'query',
action:'listmachines_ajax',
params:'\'database=\' + escape(this.value)',
onComplete:'updateMachine(e)')}"/>
<g:select id="machine" name="machine"/>

<g:submitToRemote action="showTimeLine_ajax" update="graph_content" value="execute query"/>
</g:form>

Tuesday, July 14, 2009

Groovy and Grails - why assert is great

well as always when you work with grails, well you work with closures in the controller and want an easy way to check if the parameter you require actually exist.

Now there are different ways to check for this, the old and long java version:


if(params.get("myField") == null){
throw new RuntimeException("please provide the\"myField\" argument");
}


now the assert statement makes this a lot easier to read and saves code


assert params.myField != null. "please provide the \"myFiled\" argument"


now isn't this simpler?

Monday, July 13, 2009

SetupX - bug repository

so the first step of taking an existing project over is to setup a repository to track bugs and changes to make it easier to discover problems by the users.

The new setupx bug repository can now be found here

What's more expensive IN or BETWEEN statement

This morning I spend a couple of hours tuning and testing different SQL statements to find the fastest way to fetch some data for a real time application.

While doing this I discovered that the IN query cost slightly more than the BETWEEN query.
It's not a lot, but still something.

IN query:


explain select sample_name,class,date_of_import,date from samples where visible = 'TRUE' and sample_name like '%q%' and date in( '5/26/2009 12:00:00 AM', '6/11/2009 12:00:00 AM','5/26/2009 12:00:00 AM','2009-06-01 12:00:00 AM') order by 1 desc


result:

QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort (cost=8126.07..8126.42 rows=139 width=37)
Sort Key: sample_name
-> Seq Scan on samples (cost=0.00..8121.12 rows=139 width=37)
Filter: (((sample_name)::text ~~ '%q%'::text) AND ((visible)::text = 'TRUE'::text) AND (date = ANY ('{"2009-05-26 00:00:00","2009-06-11 00:00:00","2009-05-26 00:00:00","2009-06-01 00:00:00"}'::timestamp without time zone[])))

4 record(s) selected [Fetch MetaData: 0/ms] [Fetch Data: 0/ms]

[Executed: 7/13/09 1:11:46 PM PDT ] [Execution: 2/ms]





BETWEEN query:


explain select sample_name,class,date_of_import,date from samples where visible = 'TRUE' and sample_name like '%q%' and date between '5/26/2009 12:00:00 AM' and '6/11/2009 12:00:00 AM' order by 1 desc


Result:


QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort (cost=8123.15..8123.32 rows=67 width=37)
Sort Key: sample_name
-> Seq Scan on samples (cost=0.00..8121.12 rows=67 width=37)
Filter: (((sample_name)::text ~~ '%q%'::text) AND (date >= '2009-05-26 00:00:00'::timestamp without time zone) AND (date <= '2009-06-11 00:00:00'::timestamp without time zone) AND ((visible)::text = 'TRUE'::text))

4 record(s) selected [Fetch MetaData: 0/ms] [Fetch Data: 0/ms]

[Executed: 7/13/09 12:53:32 PM PDT ] [Execution: 2/ms]

Friday, July 10, 2009

OSX and SuperDuper - bug on raid systems

If you hapend to restore your superduper backup to a raid system you will encounter a small bug which prevents you from booting your nearly restored system.

For some reason super duper cannot choose the active device to boot from, if it's a software raid. And it result's in a hanging system at boot.

Fix

go into system preferences before you boot and set your raid as the boot device. This should make it work.

Tested on
  • raid 1 of 2 disks
  • raid 0 of 4 disks (450mb/s transfer rate)

Thursday, July 9, 2009

Restoring OSX, backups are great

well my mac pro was running happily since january 2008 on a raid 10 configuration without any major issues. Sure it forgot it's keyboard from time to time and once a week I have to recharge all batteries. But otherwise it was the most reliable pc I ever had.

The wonderful timemachine software also did it's best to keep my backups up to date. So I never worried about a thing.

Till 2 harddrives started to report read/write errors 2 weeks ago. So I ordered a couple of new harddrives (500Gb x 4, I need speed and not space on this thing, and they are supposed to be nearly as fast as velicio raptors) To replace them.
At the time I noticed that I need more speed and 200mb/s write speed is just not fast enough for my stuff. So I decided to replace all 4 old harddrives with the 4 new ones in a raid 0 configuration.

I like to live risky? Sure, but I got backup's and timemachine is a great piece of software to restore a system...

Ok the idea was nice, but it turned out that you have to boot from the original osx cd to get timemachine to restore the complete system.

So plan B was on the table, let's call superduper to the rescue and use timemachine only for archiving my data.

Basically superduper generates a complete image of my system on an external harddrive and if my system fails, I can boot this HDD, keep working, while my system restores in the background.

And it only cost 27$ and makes live so much easier for me.

My new backup plan?

  • hourly timemachine archives
  • weekly super duper images

OSX and the Keyboard Pain

well it turns out you can only use apple WIRED keyboards to execute boot flags.

You also have to press the button before and during the 'boing'. And you need to keep it pressed!

Now I was thinking you could use just a windows Keyboard. But no it won't work reliable.

Now if you happen to own a wireless bluetooth apple keyboard, like I do. Be prepared to order a second wired keyboard to be able to execute commands before the OS is up.


Conclusion:


lame...

Mac OSX Boot options

taken from here

Boot key combinations:

Everybody knows about some of these boot key combinations, but some of the more obscure combinations have been long forgotten (like how many of us have a Quadra AV and use a TV as a monitor!) – Check these out, you may need one of them someday!

  • C : Forces most Macs to boot from the CD-Rom drive instead of the internal hard drive. Only works with Apple ROM drives and with bootable CD discs.
  • D : Forces the first internal hard drive to be the startup disk.
  • N : Netboot (New World ROM machines only) – Looks for BOOTP or TFTP Server on the network to boot from.
  • R : Forces PowerBooks to reset their screen to default size (helpful if you’ve been hooked up to an external montior or projector!)
  • T : Target Disk Mode (FireWire) – Puts machines with built-in FireWire into target Disk mode so a system attached with a FireWire cable will have that device show up as a hard drive on their system. Very useful for PowerBooks!
  • Mouse Button Held Down : Ejects any mounted removable media.
  • Shift : Disables all extensions (Mac OS 7-9), or disables Login items when using Mac OS X 10.1.3 or later. Also works when booting Classic mode up just like you were using the OS natively.
  • Option : When using an Open Firmware "New World ROM" capable system, the System Picker will appear and query all mounted devices for bootable systems, returning a list of drives & what OS they have on them. On "Old World" systems the machine will simply boot into it’s default OS without any Finder windows open.
  • Space bar : Brings up Apple’s Extension Manager (or Casady & Greene’s Conflict Catcher, if installed) up at startup to allow you to modify your extension set.
  • Command-V : Boots Mac OS X into "Verbose Mode", reporting every console message generated during startup. Really shows what’s going on behind the scenes with your machine on startup!
  • Command-S : Boots Mac OS X into "Single User Mode" – helpful to fix problems with Mac OS X, if necessary.
  • Command-Option : Rebuilds the Desktop (Mac OS 7-9).
  • Command-Option-P-R : Erases PRAM if held down immediately after startup tone. Your machine will chime when it’s erased the PRAM, most people will hold this combination for a total of 3 chimes to really flush the PRAM out.
  • Command-Option-N-V : Erases NVRAM (Non-Volatile RAM). Used with later Power Macintosh systems mostly.
  • Command-Option-O-F : Boots the machine into Open Firmware (New World ROM systems only).
  • Command-Option-Shift-Delete : Forces your Mac to startup from its internal CD-ROM drive or an external hard drive. Very helpful if you have a 3rd party CD-ROM drive that is not an Apple ROM device.
  • Command-Option-Shift-Delete-#(where #= a SCSI DEVICE ID) : Boot from a specific SCSI device, if you have your 3rd party CD-ROM drive set to SCSI ID 3, you would press "3" as the # in the combination.

And, the obscure ones :>) Older computers only, on some.

  • Command-Option-I : Forces the Mac to read the disc as an ISO-9000 formatted disk
  • Command : Boots with Virtual Memory turned off.
  • Command-Option-T-V : Forces Quadra AV machines to use TV as a monitor.
  • Command-Option-X-O : Forces the Mac Classic to boot from ROM.
  • Command-Option-A-V : Forces an AV monitor to be recognized correctly.

Hacking SetupX - working on the sql tables instead of hibernate

Currently one of my jobs is to get data out of the setupX system, since the developer left us and nobody has a clue how it actually works.

So far the basic understanding is that the system has one major table and all queries are executed by building sub queries in it.

Advantage of the technique:

it's extremely flexible,
Disadvantage?

It's ridiculous slow and a nightmare to figure out, since you got so many possible sub graphs.

Don't we love reverse engineering an existing software?

So this is just a small collection of statements to get data out.

get list of available questions and attributes


select question from formobject group by question;


get the list of registered species


select value from formobject where question = 'species' group by value;



The final idea is to make a snapshot of the database every night and transform it into a real database structure. Like having several tables and so.

Ajax and Grails and YUI

currently I'm trying to work more with ajax and grails.

So the first serious project is our quality control webfrontend which needs fancy stuff like

  • progress bars
  • multiple layers, cause they are hip...
and so.

So far one issue was to display a webpage in a layer on top of another webpage and the current soultion is:

controller:


class CompoundController {


/**
* shows the binbase compound
*/
def show_ajax = {
log.info("calling url...")
def id = params.id
def database = params.database

def server = System.getProperty("application.server")
def link = "http://${server}:8080/binbase-compound/bin/show/${id}?db=${database}"
//build our url
log.info("generated url: ${link}");
URL url = new URL(link)
InputStream stream = url.openStream()
Scanner scanner = new Scanner(stream)

//store the output, since this url can take a while to load, if it's not in the server cache
StringBuffer buffer = new StringBuffer()
while(scanner.hasNextLine()){
buffer.append(scanner.nextLine())
}

log.info("render result");

render "<iframe src="$%7Blink%7D" width="\"100%\"" height="\"100%\""></iframe>"
}
}

view:



<g:remotelink onloading="load();" oncomplete="done();" onsuccess="showFiles();" update="window_content" controller="compound" method="show_ajax" params="[database:database,id:id]">199651</g:remoteLink>



our div where we display the result


<div id="window">
<div class="hd"><div class="tl"></div><span></span><div class="tr"></div></div>
<div class="bd"><div id="window_content"></div></div>
</div>



and the related java script, yes I said it I'm using some javascript. Mostly the YUI library.



YAHOO.namespace("example.container");

function load() {

if (!YAHOO.example.container.wait) {

// Initialize the temporary Panel to display while waiting for external content to load

YAHOO.example.container.wait =
new YAHOO.widget.Panel("progress",
{
fixedcenter: true,
close: false,
draggable: false,
zindex:4,
modal: true,
visible: false
}
);

YAHOO.example.container.wait.setHeader("
please wait, this process can take up to 5 minutes
");
YAHOO.example.container.wait.render(document.body);

}


YAHOO.example.container.wait.show();
}

function done() {
YAHOO.example.container.wait.hide();

}

function showFiles() {

if (!YAHOO.example.container.files) {

// Initialize the temporary Panel to display while waiting for external content to load

YAHOO.example.container.files =
new YAHOO.widget.Panel("window",
{
fixedcenter: true,
close: true,
draggable: false,
zindex:5,
modal: false,
visible: false
}
);

//YAHOO.example.container.files.setHeader("Files for day...");
//YAHOO.example.container.files.setBody("<div id=\"showFiles\"></div>
");
YAHOO.example.container.files.render(document.body);

}


YAHOO.example.container.files.show();
}



and than there is some css,


#window.yui-panel .bd {
overflow: hidden;
padding: 0px;
border: 1px solid #aeaeae;
background-color: #FFF;
width:1024px;
height:600px;
}

#window.yui-panel .ft {
font-size: 75%;
color: #666;
padding: 0px;
overflow: hidden;
border: 1px solid #aeaeae;
border-top: none;
background-color: #dfdfdf;
}

#window.yui-panel .hd span {
vertical-align: middle;
line-height: 22px;
font-weight: bold;
}

#window.yui-panel .hd .tl {
width: 7px;
height: 22px;
top: 0;
left: 0px;
position: absolute;
}

#window.yui-panel .hd .tr {
width: 7px;
height: 22px;
top: 0;
right: 0px;
position: absolute;
}
#progress.yui-panel .bd {
overflow: hidden;
padding: 0px;
margin: 5px;
background-color: #FFF;
}

#progress.yui-panel .ft {
font-size: 75%;
color: #666;
padding: 0px;
overflow: hidden;

border: 1px solid #aeaeae;
border-top: none;
background-color: #dfdfdf;
}

/* Provide skin for custom elements */
#progress.yui-panel .hd span {
vertical-align: middle;
line-height: 22px;
font-weight: bold;
padding-left: 60px;
padding-right: 60px;
padding-top: 30px;
padding-bottom: 30px;
border: 1px solid #aeaeae;
background-color: #dfdfdf;
width: 120px;
height: 60px;
}

#progress.yui-panel .hd .tl {
width: 7px;
height: 22px;
top: 0;
left: 0px;
position: absolute;
}

#progress.yui-panel .hd .tr {
width: 7px;
height: 22px;
top: 0;
right: 0px;
position: absolute;



I'm still not a fan of css/js, but sometimes you gotta use what's available.

MockingSpring - Testing ServletFiltes

mocking spring,

well it's hot in davis and I have no AC, so what's better than to run into the nice and cold office and playing with some junit tests.

Basically how to junit test servlet filters. Like always somebody smart wrote some great tools for me to use.

An example of a simple servlet filter which forwads all access to a cutom report factory


package edu.ucdavis.genomics.metabolomics.binbase.filter;

import edu.ucdavis.genomics.metabolomics.util.status.Report;
import edu.ucdavis.genomics.metabolomics.util.status.ReportFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

import org.apache.log4j.Logger;

/**
* forwards all requests to the reporter which deals with it
*


* User: wohlgemuth
* Date: Jun 28, 2009
* Time: 3:33:00 PM
*/
public class IPLogger implements Filter {

/**
* forwards everything to the server
*/
private Report report;

/**
* logging nstances
*/
private Logger logger = Logger.getLogger(getClass().getName());

/**
* initialize the connection to the report method
*
* @param filterConfig
* @throws ServletException
*/
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("initialize filter");

//access the factory we need for the reporter
String factory = filterConfig.getInitParameter("factory");

if (factory == null) {
logger.info("no factory provided, so we use the default factory");

report = ReportFactory.newInstance().create(IPLogger.class.getSimpleName());
} else {
logger.info("using factory: " + factory);
report = ReportFactory.newInstance(factory).create(IPLogger.class.getSimpleName());
}
}

/**
* does the actual filtering
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String host = servletRequest.getRemoteHost();
String address = servletRequest.getRemoteAddr();
int port = servletRequest.getRemotePort();

String uri = "unknown";
if (servletRequest instanceof HttpServletRequest) {
HttpServletRequest req = (HttpServletRequest) servletRequest;

uri = req.getRequestURI();
}

//send it out so that we can deal with these event in a different class or so
report.report(new IPRequest(host, uri, address, port), FilterReports.FITLER_URI_ACCESS, FilterReports.FILTER_REQUEST);
}

public void destroy() {
}
}



and the junit test to test it, without a web server or so.


public class IPLoggerTest {

private IPLogger filter;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MockFilterChain chain;

@Before
public void setUp() throws Exception {
filter = new IPLogger();
MockFilterConfig config = new MockFilterConfig();
config.addInitParameter("factory", TestFactory.class.getName());
filter.init(config);
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
chain = new MockFilterChain();
}

@After
public void tearDown() throws Exception {
}

@Test
public void testDoFilter() throws IOException, ServletException {
request.setRequestURI("http://127.0.0.1/test.html");
filter.doFilter(request, response, chain);


}
}



and our little factory which does the acutal testing of the received object


public class TestFactory extends ReportFactory{


private Logger logger = Logger.getLogger(getClass());
/**
* checks if all the result's fit the exspectations
*
* @param properties
* @param s
* @return
*/
public Report create(Properties properties, String s) {
return new Report() {
public void report(String s, Serializable serializable, ReportEvent reportEvent, ReportType reportType, String s1, Date date, Exception e) {
logger.info("received: " + serializable);
assertTrue(reportEvent.equals(FilterReports.FITLER_URI_ACCESS));
assertTrue(reportType.equals(FilterReports.FILTER_REQUEST));

assertTrue(serializable instanceof IPRequest);
IPRequest request = (IPRequest) serializable;

assertTrue(request.getHost().equals("localhost"));
assertTrue(request.getUri().equals("http://127.0.0.1/test.html"));
assertTrue(request.getAddress().equals("127.0.0.1"));
assertTrue(request.getPort() == 80);
}

public String getOwner() {

return IPLogger.class.getSimpleName();
}
};
}
}

Grails and some annoying CSS in the Body Part

the last 72h I spend with debugging some css, because my webpage did not show in the way I wanted it to show.

So long story short, for some reason grails defines several body elements in the main.css file

body {
background: #cccccc;
color: #333;
font: 11px verdana, arial, helvetica, sans-serif;

width:100%;
height:100%;

}


.body {
float: left;
margin: 0 15px 10px 15px;
}

and the 'float:left' in the second body tag wrecked havoc with my application...

...great 3 days wasted on lay outing...

Creating a minimum and a maximum Date for a Calendar

If there is one thing I hate it's to work with dates. Not that it's hard or so. Just if you want to work with the java core classes it's sometimes a hassle.

For example I needed the minimum and maximum date for a day. After a lot of googleling I came up with a simple? solution.


Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();

c1.set(Calendar.AM_PM,Calendar.AM);
c1.set(Calendar.HOUR, c1.getActualMinimum(Calendar.HOUR));
c1.set(Calendar.MINUTE, c1.getActualMinimum(Calendar.MINUTE));
c1.set(Calendar.SECOND, c1.getActualMinimum(Calendar.SECOND));
c1.set(Calendar.MILLISECOND, c1.getActualMinimum(Calendar.MILLISECOND));

c2.set(Calendar.AM_PM,Calendar.PM);
c2.set(Calendar.HOUR, c2.getActualMaximum(Calendar.HOUR));
c2.set(Calendar.MINUTE, c2.getActualMaximum(Calendar.MINUTE));
c2.set(Calendar.SECOND, c2.getActualMaximum(Calendar.SECOND));
c2.set(Calendar.MILLISECOND, c2.getActualMaximum(Calendar.MILLISECOND));

Date first = c1.getTime();
Date last = c2.getTime();


Result:

Tue Jul 07 00:00:00 PDT 2009
Tue Jul 07 23:59:59 PDT 2009

The important part is here:


c1.set(Calendar.AM_PM,Calendar.AM);


if you don't specify AM/PM you end up with:

Tue Jul 07 12:00:00 PDT 2009

which is a bit annoying...

Installing SetupX under ubuntu

a first try to write a connector to getch all kinds of informations, using a hibernate layer.

how to:
  • install your plain old Ubuntu server box
  • install java 5: apt-get install sun-java5-jdk
  • install subversion: apt-get install subversion
  • install unzip: apt-get install unzip
  • install ant: download ant 1.6.5, unzip it and add it to the path
  • install jboss: download jboss-4.2.3.GA, unzip it in your directoy of choice, let's use opt
  • get setupX:
    svn checkout -r421 http://setupx.googlecode.com/svn/trunk/ /opt/setupx
  • install mysql: apt-get install mysql-server
  • start the mysql server: /etc/init.d/mysql start
let's configure setupX:
  • lims/setup/setup.properties - general configuration
  • lims/setup/resources/system/config.properties - configure where jboss sits
  • lims/setup/resources/system/system.properties - configure email settings and jboss again. Also specify your data directories
let's start our first install
  • go into /opt/jboss/seupx/lims
  • execute 'sh firstinstall.sh'
at this point we run into several issues, since we want to install setupx in '/opt/setupx' and not '/usr/share/setupx'.


[java] Util_____________: DEBUG: Using org.xml.sax.parser : org.apache.xerces.parsers.SAXParser
[java] org.setupx.repository.SettingsUnavailableException: java.io.FileNotFoundException: /usr/share/setupx/lims/conf/system/system.properties (No such file or directory)

This is directly related to a bug in the class "org.setupx.repository.Settings" and can be fixed with a simple change in this file to reflect our directory.

This is a nice example why it's considered to be bad practice to hard code file paths.


private static File file = new File("/opt/setupx/lims/conf/system/system.properties");

Since we already created the database with the 'firstinstall.sh' script we now just need to go into setupx/lims/setup and execute:

sh setup.sh

After the installation finished we could now access setupX under:

http://****:8080/xx1/

your setupx username is the set email address and your password the same.

review:

The installation took a lot of effort to get it working and is not yet perfect. I think the first thing which should be done is to write a graphical ant installer which modifies the files for you, to make this process a bit easier.
The main issue is really the absolute file path in the one java class.

Time cost: a bit over an hour and 30 minutes.

yet another blog

this blog is basically about my coding expirience, since people started to complain that I write to much about work and coding stuff in my other blog