Monday, December 15, 2008

JPA: Entity as identity and unidirectional OneToMany

Recently, I have been trying to model an existing database using Java Persistence API. I have tried both OpenJPA and Hibernate, settling on the latter because it gives more informative exceptions and errors. OpenJPA however has better documentation than Hibernate JPA, in my opinion. Therefore I will refer to both in this article. Forgive me for the rather long title, it sums up some of the experiences I've made.

I could have written quite lengthy about entity as identity, compound identities and relations, but to save time, both yours and mine, I will provide an example:

Two classes Picture and Comment are related to one another. One picture is identified by an integer. In addition, a picture has a set of comments. One comment is identified by a picture and an integer. Thus, a comment can not be identified without a picture, and therefore comments only exists in the relation between a picture and its comments.

JPA implementations default to using a table for every entity and a join-table for their relations. In my example, the pictures are in their own table but the comments are in the same table as the relation. In other words, the comments table has a foreign key to the pictures table giving the owning picture. Thus, the tables are:

pictures: id, user, title, description
comments: pid(picture id), id, user, comment

The user field is another relation, which is trivial and therefore won't be discussed any further. The primary key of "pictures" is the id field, while the primary keys of the comments are the "pid" and the "id". Thus a "pid" and an "id" identifies a comment. The "id" field of the comment could also be called the comment number.

Now, I tried modelling this in several ways, using for instance Hibernates CollectionOfElements annotation. I explored other ways since this method is specific to Hibernate. The way I settled on, uses only the JPA standard annotations, but I'm not entirely sure why it works. The key to making it work was using the @MapKey annotation, which I have not seen used on anything other than Maps. I should investigate more about the @MapKey annotation. Without further explanation, these are the two classes I settled on. Getters and setters are omitted. Any comments you may have are appreciated!



@Entity
@Table(name="pictures")
public class Picture implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;

@ManyToOne
@JoinColumn(name="uid")
private User user;
@Basic
private String title;
@Basic
private String caption;

@OneToMany
@MapKey
@JoinColumn(name="pid")
private List<picturecomment> comments;
}

@Entity
@Table(name = "pictures_comments")
@IdClass(PictureComment.PictureCommentId.class)
public class PictureComment implements Serializable {

@Id
private int id;
@Id
@Column(name="pid")
private int pictureid;

@ManyToOne
@JoinColumn(name = "uid")
private User user;
@Basic
private String comment;



public static class PictureCommentId implements Serializable {
@Column(name="pid")
public int pictureid;
public int id;

public boolean equals(Object o) {
if (!(o instanceof PictureCommentId)) {
return false;
}
PictureCommentId p = (PictureCommentId) o;
return pictureid == p.pictureid && id == p.id;
}

public int hashCode() {
return id ^ pictureid;
}
}
}




Hibernate generates the following SQL statement:
select comments0_.pid as pid2_, comments0_.id as id2_, comments0_.id as id4_1_, comments0_.pid as pid4_1_, comments0_.comment as comment4_1_, comments0_.uid as uid4_1_, user1_.id as id2_0_ from pictures_comments comments0_ left outer join users user1_ on comments0_.uid=user1_.id where comments0_.pid=127

Some links to the resources which I found useful:
http://www.hibernate.org/118.html
http://www.jpox.org/docs/1_2/jpa_orm/one_to_many_map.html
http://openjpa.apache.org/builds/1.2.0/apache-openjpa-1.2.0/docs/manual/manual.html#ref_guide_pc_oid_entitypk

Friday, June 13, 2008

Java SE JSR 82 Bluetooth Linux implementation

Long title, huh? I just want to spread the word that the excellent and open source bluecove JSR 82 implementation is available for both Linux and Windows. I found it searching for a Linux JSR 82 implementation using BlueZ.
http://snapshot.bluecove.org/

Saturday, June 7, 2008

Java and Jpeg Metadata

How does one get the time of capture from a jpeg image? Sounds easy enough. The answer however was not all too obvious. I always try to find a solution using only the jdk, without adding new libraries to my projects, and this seemingly small issue wasn't going to lead me astray from that goal.

My first few steps was trying ImageIO. To get an iterator of readers for my ImageStream, I used ImageIO.getImageReader(imageStream). Now, I'm not picky so I'll just take the first reader in the iterator with iterator.next(). Then to get to the metadata I used reader.getImageMetadata(0), since I only have one image in my imagestream.

Metadata however comes in different formats. And the metadata object i got from the ImageReader reported it had these format names to offer:

javax_imageio_jpeg_image_1.0
javax_imageio_1.0

To get to the metadata, you use one of the format names as the argument to your IIOMetadata object's getAsTree method. This gives you an XML DOM root node, which you use to traverse a metadata tree. The first format in the list seemed promising with jpeg in its name and all, but all the interesting metadata like time of capture isn't parsed. Instead it is dumped in a DOM node called "unknown". The second format doesn't seem to give you anything useful.

This seemingly hopeless discovery resulted in a lot of googling. Several libraries have been made for parsing exif metadata, with Drew Noakes Metadata Extractor and Sanselan looking like the best options. But as I said earlier, I don't like adding a new library every time I bump into something without an obvious solution.

Apparently, Sun has a project called Java Advanced Imaging Image I/O Tools, which provides plugins for the tiff image format among others. Since the exif metadata inside the jpeg image, really is the same as tiff metadata, I installed the library and tried reading the image with the tiff reader. This resulted in a lot of exception throwing and it didn't seem to want to work. Maybe it would have worked if I had isolated the exif data in some way.

Almost by accident, I noticed that the jpeg reader returned from getImageReader suddenly had another format name to offer:
com_sun_media_imageio_plugins_tiff_image_1.0
When parsing the metadata tree using this format, all the metadata you could ever want suddenly appeared before me. Installing jai-imageio-tools must have added a tiff/exif reader for the jpeg ImageReader.

So the solution for me was to install Java Advanced Imaging Image I/O Tools and use the com_sun_media_imageio_plugins_tiff_image_1.0 metadata format. Technically, this involved adding another library, but it extends the standard java library in such a way that I don't need to import or use a single class outside it.

You should also check out this document to aid you in all your exif parsing activities: http://www.exif.org/Exif2-2.PDF ( It tells you the names and numbers of all the metadata fields ).

Wednesday, May 14, 2008

Java: Programmatically sign generated SSL certificates

I spent the major part of an afternoon trying to figure out how to do basic certificate authority operations, like certificate signing, programmatically in Java. After checking out the JDK source I found that sun.security.x509.X509CertImpl supports signing by a private key. Google found this code example for me:

http://www.java2s.com/Code/Java/Security/CertificateSigner.htm

Now that should fend off some 3rd party libraries for a while :)

Tuesday, May 13, 2008

SSL with FORM fallback

I struggled for a while to get SSL authentication with FORM fallback to work on tomcat. Sounds like something which must have been already implemented by someone, right? The latter is also true. The following are implementation and usage articles on the tomcat wiki:

Implementation:

http://wiki.apache.org/tomcat/SSLWithFORMFallback6

Usage:

http://wiki.apache.org/tomcat/SSLWithFORMFallback

For the usage part, it should suffice to just comment out the auth-method in your web.xml login-config.

Thursday, April 24, 2008

Opera 9.5 Beta 2

Opera 9.5 Beta 2 is out. The reason I am excited about this is that I have an x86_64 processor running linux. Opera 9.5 is the first version of Opera to be 64 bit compatible. Finding the x86_64 binary on their website however isn't all that easy. To find it, you have to navigate the mirror site which you have selected to download from. I used this one:

ftp://opera.nsc.no/pub/nsc.no/mirrors/operasoftware/linux/950b2/final/en/x86_64/


The easiest way to try beta 2 is to download the tar.gz file, unzip to your favourite folder and just run the opera executable.

Read about the next version of Opera here.

Happy browsing!

Sunday, October 7, 2007

HowTo: OpenSUSE 10.3 With Compiz Fusion

I had a very painful process getting OpenSUSE 10.3 with Compiz Fusion working. These are the steps which worked for me:

Hardware:
Compal Multinote HGL30 laptop
Nvidia Geforce GO 7600 graphics
Intel Core 2 Duo 2ghz processor

Warning: I'm translating the menu and button texts from Norwegian to English and you might find that it doesn't correlate with the actual English version.

I'm dividing the steps so that in the event of a crash you would have a better chance of knowing which part failed.

(1) Install OpenSUSE by booting DVD(I'm using the x86_64 version).

Select online archives.
Select KDE (Gnome failed every time trying to download some msttfonts-patch xml file).
Agree to updating.

(2) Activate repositories

Open YaST2 control center. Go to software and community repositories. Activate the following repositories:

Main Repository(NON-OSS)
Main Repository for updates
Main Repository(OSS)
NVIDIA Repository
Packman Repository
VideoLan Repository
OpenSUSE BuildService - X11:XGL

Note: All of these might not be necessary.

After adding these it might be a good idea to right click the update icon in the systray and click "Check Now".

(3) Install NVIDIA Driver

The 1-Click install did not work at all for me. I had multiple problems including xorg not finding the nvidia module when starting. Instead I used the "Install programs via Zen" application. Search for nvidia, check "x11-video-nvidiaG01" ( not the legacy x11-video-nvidia ) and confirm. Log out and log back in to make xorg use the new driver.

Make a backup of your xorg.conf config file.

Type the following commands in a terminal window as root:
nvidia-xconfig --composite
nvidia-xconfig --render-accel
nvidia-xconfig --add-argb-glx-visuals

Log out and log back in to apply the changes.

(4) Install Compiz Fusion

Click "Install programs via Zen".
Search for compiz.
Check compiz-fusion-kde and confirm.

Open up a terminal window and type:
ccsm &

This will open up the compiz settings manager. Click the effects category and find "Window Decoration".

Enter the following in the "command" field:
kde-window-decorator --replace &

Close the settings manager, open up a terminal window and type:
compiz ccp --replace &

Admire your desktop. :)

Additional steps would be making the "compiz ccp --replace &" command start when logging in. This can be accomplished by putting an executable script with the above command in the ~/.kde/Autostart/ directory.

If your kde desktop switcher doesn't play nice with the compiz fusion desktop switching shortcuts, try running the command(as a regular user):
dcop kicker kicker restart

This is mostly just a memo for my own use. Please add your own comments if you see any errors in the article.