Sunday, January 25, 2009

Struts 2/dojo datepicker tag sucks with non US locale

I have been messing around with Struts 2 datepicker tag. The goal is to submit date and time for a date field.

You make a Date field in Struts 2 action object. And you put in your JSP the datepicker tag like this:

<s:datetimepicker name="date_planned_date" label="" displayformat="dd.MM.yyyy" type="date" value="%{date_planned_date}" />
Now it is important not to get the locales mixed up. So I have been forcing the locale on EVERY form like that:

<s:hidden name="request_locale" value="et_EE" />
And just in case I have my locale also specified in struts.xml:

<constant name="struts.locale" value="et_EE" />
Anyway this setup doesn't work, because datepicker is not able to parse the date in the "value" param. So we omit the value param and things start working. But in our case we NEEDED the value param, because we also wanted to submit time but we didn't want to use the built-in TIME picker, but instead a text-field.

So the solution for us was to make two separate String fields in our Struts 2 application to wich we submited the date and time part of the datetime. And upon saving to DB we constructed the date out of those string fields.

With s:datepicker any "out of the box" setup for i18n does not work and as always with Struts customizations need a lot of extra coding. The datepicker tag should just work with ANY date it is given with the "value" param. Currently it does not - Java's hard work and no fun!

Wednesday, January 21, 2009

Hibernate query logging with binded parameters

I've been struggling with Hibernate queries before. Now I took the time to research this and ended up with another fucked up situation in Java world. There really is no "best practice" AND easy way of capturing queries (HQL/SQL) that are executed on DB in Hibernate.

Basically there is these alternatives:

1. Hibernate built in options ( a good description here )
2. Use Jamon, P3Spy or another similar tool
3. Custom query translator for Hibernate ( see here )
4. SQL database general query log (if applicabel to your database)

Among these only P3Spy, Custom translator, and query log have the ability to display binded query parameters within the query. So that you can copy/paste the query to your SQL tool/terminal. P3Spy project is on ice and custom query translators don't really sound like "best practice". General query log depends on DB. And should you really be spending your time programming custom translators?

Not least but last: the methods above display ALL the queries executed! When using Hibernate built-in options the data flow is way too moch for a web page that has maybe 20 queries on page load.

So there is NO NICE way of debugging that one runaway query in Hibernate/JPA. This is just NOT ACCEPTABLE. We need to be able to turn debugging on/off in Java code when making queries. Heck, my custom built data layer in PHP has the ability!

Tuesday, January 20, 2009

Struts 2 JSP select tag woes

Today I have been sucking around with a Struts 2 tag that is really selfish. I need a multiple select that has preselected values.

Now since I don't want to spend much time for this implementation I am saving these values in a text field in database. And please don't tell me that there is another "correct" way of doing it (maybe saving the values as a realations in a table). Now the items in dropdown come from DB table with primary key ID-s as dropdown values.

And now we get to the fun part - Struts2 "s:select" tag is type safe! Since my list values come from DB primary keys they are integers. Since I want to store the selected values in a text field, they are strings. If I do string.split(), I still get strings. Struts 2 does'nt have a simple solution to this. I just have to save the same type of values in DB or convert the type of the values someplace. So there is no one-line approach to make preselected values in this setup.

---rant on---
Message to Struts/JSP developers. In the view level there has to be simple solutions to these kind of problems or Struts just will be tossed aside. So please, make a simple parameter to s:select that forces comparing strings whatever the source types are. Ups Struts is open source so I might just as well blame myself :p .
---rant off---

Friday, August 1, 2008

Struts 2 and Hibernate gotcha

This post will be a short one, since struts2/hibernate has taken already too much of my time lately :( .

We have a web application set up like this example here . For some time now I have been wrestling with a problem that right after insert joined data for my Entity bean does not appear in my JSP. The same problem is documented in this thread here.

I've been actively searching for a solution to this problem for a few days now. As it turns out the culprit is Hibernate caching. You can find a small overview of Hibernate caching here . For us the simple solution was to turn off hibernate second level cache altogether. And now our applicationContext.xml has this in it:


<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource">
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL">
<property name="showSql" value="true">
</property>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.cache.use_second_level_cache" value="false">
<entry key="hibernate.cache.use_query_cache" value="true">
<entry key="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider">
<entry key="hibernate.show_sql" value="true">
<entry key="hibernate.use_sql_comments" value="false">
<entry key="hibernate.format_sql" value="false">
</entry></entry></entry></entry></entry></entry></map>
</property>
</bean></property></property></bean>

Now that was productive for a few days work - wasn't it? Extremely user friendly indeed!!!

Wednesday, July 16, 2008

Cometd, Tomcat and Jetty - the road to server push

I have managed to "Hello world" the cometd Java implementation following this guide and hacking away. No it didn't work out of the box, if you want to get it working also look at the comments and specifically the one made by "erkulas" (me).

I just had to implement a ultra-simple chat application as soon as I got things working. In fact it took about 3 lines of Javascript code and a Cometd server running on Jetty running in Tomcat. Thats actually very impressive IMHO. With just basic knowledge of Eclipse/Java and a few JAR-s you can make a chat application that runs directly in browser with 5 lines of Javascript code and within 5 minutes.

The the demo that I prepared is here . Just launch 2 or more different browsers (IE and Firefox and Opera) and go to this URL. Everything you type in one browser will be visible in another with Cometd pushing the data to every browser.

Friday, July 4, 2008

Struts 2 anemic domain models with POJOs

The other day I was integrating Easydealer.net PHP code and Java modules. The goal is to gradually move most Easydealer core code to Java (Struts 2, Spring, Hibernate). But since there is a lot of legacy code written in PHP we must be able to code some stuff also in PHP.

Since the browser is the user interface we had a simple solution - make two logins and create user sessions with Java and PHP backends with one login form. Then we can decide with any click if we want to code it in Java or PHP. No problem - said, done (with Iframe btw.).

Now I was modifying the same database tables with Java and PHP. In PHP we use PEAR_DB for databse abstraction and PEAR_DB has its own sequence schema for primary keys. It keeps the last sequence number for a table in a little table named "tablename_seq". Now if I needed to insert data from Struts I had to generate the primary key with the same schema as PEAR_DB . OK. No problem here, we just code it in our service layer.

But wait! That way I'll have to specifically code it for all the tables I am inserting in all the service classes that deal with database insert/update. That sucks. Should'nt all the domain classes just "know" about their primary keys? It makes no sence for the service layer to tell the domain classes about their identity. This code should be in our domain model!

So alternatively I could program the code that deals with PEAR_DB "_seq" tables in getters/setters of the domain model classes. And that brings us to the subject. The simplest struts 2 domain model objects contain fieldnames, getters/setters and some annotations for relations. But all that information can be as well described in a list or map . In our PHP code all the database model is described in one big array. Ok some getters/setters have a little business logic in them. But that's it. So i have ended up with an anemic domain model, wich is basically a description of my database structure in POJO-s. That's not my goal!

While searching for alternatives I found this little gem . It is a bit dated, but there are practically all the alternatives analysed. And the conclusion should be - pick your poison :) .

CentOS Xen domU image resize

The impatient ones can find the clean solution with Linux commands at the end of this post. For the inquiring minds, here we go.

I've been struggling with our CentOS Xen install problems for months now. The server is running on HP Proliant DL380 G5 with two 86 GB SAS discs in RAID 1 (full mirroring).

Since we had so limited HDD space capacity (my bad when ordering the server), I had to limit the space allocated to Xen domU-s . Each got about 6-10 GB . Now we are running into space problems on these domU-s. As it turns out there is NO straightforward procedure for resizing domU disks on Xen and Redhat (um. i meant CentOS :-P ).

We installed domU-s by accessing install media through HTTP (Apache on dom0). This post is not about domU installation so I'll refrain from describing the procedures with detail. But this had a small but VERY SIGNIFICANT consequence. Installing domU-s this way we had an ordinary CentOS install going on in dom0 and resulting in new domU . All fine and dandy, right? NOT!

This procedure ended up with an image file on dom0 containing two partitions (small one for kernel and a LVM) . Now since all the userland data is on our LVM LV (LVM Logical Volume) we wanted to add space to that. So in order to do that we needed:

1. add "physical" space to the image file from dom0
2. add/allocate that space to our LVM PV (Physical Volumn) volume inside the image file
3. add/allocate space to LVM LV (Logical Volume) inside LVM PV

Should be piece of cake ... well not quite.

Point 1 is no problem, we use "dd" on the image file dom0 and you don't even need to shutdown the domU. One possibility for points 2 and 3 would be to shutdown domU, mount it's image in dom0 and resize the LVM. BUT, our install procedure above resulted in the LVM descriptors (ID-s) of dom0 and domU LVM discs to be EXACTLY the same. So we have NO WAY of altering the LVM on domU images from dom0 because the system could only identify dom0 LVM disks. We have a chicken and an egg problem, because we can't even change the ID-s on the same system. Ok, so alternatively we will try to resize the LVM PV and LV in domU - to no avail. The "pvextend" and "lvextend" end up with error messages about not being able to modify the volumes (even on runlevel 1).

At the end of the day the only solution was to create a new primary partitition on domU (with fdisk), create a new PV and extend LV with it. Here is the trace:
---------------------------------------------------------
Dom0:
dd if=/dev/zero bs=2G count=1 >> IMAGE_FILE

DomU:
[root@mail ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/VolGroup00-LogVol00
7.2G 6.0G 851M 88% /
/dev/xvda1 99M 18M 76M 20% /boot
tmpfs 500M 0 500M 0% /dev/shm
[root@mail ~]# pvs -o +dev_size --units h
PV VG Fmt Attr PSize PFree DevSize
/dev/xvda2 VolGroup00 lvm2 a- 9.31G 32.00M 9.32G

------ fdisk (make primary partitition /dev/xvda3 on disk from free space) ------

[root@mail ~]# pvcreate /dev/xvda3 Physical volume "/dev/xvda3" successfully created [root@mail ~]# vgextend VolGroup00 /dev/xvda3 Volume group "VolGroup00" successfully extended [root@mail ~]# lvextend -L+608M /dev/VolGroup00/LogVol00 Extending logical volume LogVol00 to 17.34 GB Logical volume LogVol00 successfully resized [root@mail ~]# resize2fs /dev/VolGroup00/LogVol00 resize2fs 1.39 (29-May-2006) Filesystem at /dev/VolGroup00/LogVol00 is mounted on /; on-line resizing required Performing an on-line resize of /dev/VolGroup00/LogVol00 to 4702208 (4k) blocks. The filesystem on /dev/VolGroup00/LogVol00 is now 4702208 blocks long. [root@mail ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup00-LogVol00 18G 6.0G 11G 36% / /dev/xvda1 99M 18M 76M 20% /boot tmpfs 500M 0 500M 0% /dev/shm
---------------------------------------------------------

PS: you should be able to do this without a domU shutdown. Use it at your own risk tho.