Technical blog

March 7, 2010

Steps to create a signed applet

Filed under: java — Tags: , — paawak @ 11:23

Many times while working with Applets, you need a signed jar to circumvent the security restrictions placed on an unsigned applet. Here are three simple steps of creating a self-signed applet:
keytool -genkey
keytool -selfcert
jarsigner myapplet.jar mykey


Further reading:

http://www-personal.umich.edu/~lsiden/tutorials/signed-applet/signed-applet.html

http://java.sun.com/javase/6/docs/technotes/tools/solaris/keytool.html

January 29, 2010

Microsoft Excel date dilemma

Filed under: java — Tags: , — paawak @ 17:00

I was recently exporting a Microsoft Excel SpreadSheet to a data base. I was using Apache POI to read the excel sheet. To my utter surprise, I was getting the date as some numeric value. I initially thought it was the Unix epoch, but it turned out wrong. I then applied my “dirty-fix” talent, rather than googling. 17-Jan-09 was 39830. 39830/365 = 109. So I gathered that this Microsoft Epoch started from 1st Jan 1900. I was not entirely wrong. But it actually starts from 31st December 1899 :). So, 31st December 1899 is the Day 1 in Microsoft Terminlogy. The Microsoft guys goofed up in the leap year thing, so they lost one day. I know this as I googled a bit today, and came across this. So, the Microsoft Epoch really should have been 1st Jan 1900. Better luck next time :).

I would love to share the code with you:

import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
 
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 
/**
 *
 * @author paawak
 */
abstract class SheetReader {
 
    final HSSFWorkbook excelBook;
 
    final int startFromRow;
 
    SheetReader(InputStream excelSheetContents, int startFromRow)
            throws IOException {
 
        excelBook = new HSSFWorkbook(excelSheetContents);
        this.startFromRow = startFromRow;
 
    }
 
    String getCellContent(HSSFRow row, int columnIndex) {
 
        HSSFCell cell = row.getCell(columnIndex);
 
        String content = null;
 
        if (cell != null) {
 
            int cellType = cell.getCellType();
 
            switch (cellType) {
           // also handles dates
            case HSSFCell.CELL_TYPE_NUMERIC:
                content = String.valueOf(cell.getNumericCellValue());
                break;
            case HSSFCell.CELL_TYPE_STRING:
                content = cell.getStringCellValue();
                break;
            case HSSFCell.CELL_TYPE_BOOLEAN:
                content = String.valueOf(cell.getBooleanCellValue());
                break;
            case HSSFCell.CELL_TYPE_BLANK:
                break;
            default:
                throw new UnsupportedOperationException("HssfCellType "
                        + cellType + " not yet supported");
            }
 
        }
 
        return content;
 
    }
 
    Date getDate(String date) throws NumberFormatException {
 
        Date givenDate = null;
 
        if (date != null && !"".equals(date.trim())) {
 
            // hack to have date in readble format
            int dateInt = (int) Float.parseFloat(date);
 
            Calendar cal = new GregorianCalendar(1899, 11, 30);
 
            cal.add(Calendar.DATE, dateInt);
 
            givenDate = cal.getTime();
 
        }
 
        return givenDate;
 
    }
 
}

Note that I am getting a float for the date, which I am converting to String, for purely ease of handling.

December 25, 2009

[How-to] Enable Java in Mozilla Firefox on Linux

Filed under: java — paawak @ 00:37

#cd /usr/lib/mozilla/plugins

#ln -s /opt/java/jdk1.6/jre/plugin/i386/ns7/libjavaplugin_oji.so

If you have an existing libjavaplugin.so, make sure you unlink it, as it might cause problems.

December 9, 2009

Java chat applet for Jabber/GTalk

Filed under: java — paawak @ 01:16

Over the weekend, I have been working on a chat client based on XMPP. It will work for all Jabber servers as well as GTalk.

How it all started

A friend of mine had requested me if I could support chat on his website. I was googling a bit. I am a big fan of Jabber since its open source and based on XMPP. I came  across this page listing all Jabber clients: http://xmpp.org/software/clients.shtml, and I was trying out the web one.

I was awed by Claros Chat. Its a fabulous web-based chat client and has the look and feel of GTalk. I thought of taking advantage of its open source. So I downloaded the tar and was generally browsing through the contents when I came across smack. Now this is the core library which powers Claros. It provides a pure java implementation of the XMPP protocols. Its very well written. And it was not long before I started using it to write my own chat applet, based on Swings. People will find smack.jar and smackx.jar in its various versions in this maven repo under jivesoftware. A word of warning, the latest version 3.1.0 is still beta and did not work for me. I am using 3.0.4.

Demo time

Chat applet Login Screen

Chat applet Login Screen

Click here to view the applet.

You would need JRE 1.6 to view this. Its still in very early stages of development and there are lots of bugs. But I guess the basic functions are in there, the UI sucks though. I will fix it as soon as I get some time, meanwhile, please bear up :)

Sources

The sources can be found here. They are under the Apache License.

September 10, 2009

Accessing Java objects with Flex remoting

Filed under: flex, java — Tags: , , , — paawak @ 20:23

This post talks about how we:

  1. Query the database through Flex by invoking a server side Java object (the ItemDao) by Flex remoting with Blaze DS.
  2. Displays the result, which is a java.util.List<Item> in a DataGrid and a ComboBox.

Now that we have successfully configured Spring with Flex, we would like to access server side Java objects, which we expose through Spring.

We will query Items from the database using Spring/Hibernate through the ItemDao.

This is how my Item.java looks like:

 
/**
 * 
 * @author paawak
 */
@Entity
public class Item implements Serializable {
 
    private static final long serialVersionUID = 340952899861L;
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    @Column(length = 20, unique = true)
    private String code;
 
    @Column(length = 15, unique = true)
    private String name;
 
    @Column(length = 50)
    private String description;
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getCode() {
        return code;
    }
 
    public void setCode(String code) {
        this.code = code;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
}

And this is how my ItemDao.java looks like:

/**
 * 
 * @author paawak
 */
public class ItemDao extends HibernateDaoSupport {
 
    @SuppressWarnings("unchecked")
    public List<Item> getAll() {
        return super.getHibernateTemplate().loadAll(Item.class);
    }
 
}

As is obvious, I configure the ItemDao through Spring and then expose it as below:

 
...
<flex:remoting-destination ref="itemDao"/>
...

The one thing to remember here is that, Flex handles remote access through call back mechanism. That is, the call to the Java object, is asynchronous. The call returns immediately. However, the result (or the error) is got asynchronously by registering suitable listeners.

The following code snippet illustrates this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    initialize="initData();">
 
    <mx:Script>
        <![CDATA[           
            import mx.controls.Alert;
            import mx.rpc.remoting.RemoteObject;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.events.FaultEvent;
            import mx.collections.ArrayCollection;
 
            [Bindable]
            private var itemsMultiArray:ArrayCollection;
 
            private function initData():void {
 
                //call the getAll() method on the itemDao remote object, which has been exposed
                var itemRO:RemoteObject = new RemoteObject();
                itemRO.destination = "itemDao";
                itemRO.addEventListener(ResultEvent.RESULT, getItemListResultHandler);
                itemRO.addEventListener(FaultEvent.FAULT, faultHandler);
                itemRO.getOperation("getAll").send();
 
            }
 
            private function getItemListResultHandler(event:ResultEvent):void {
 
                itemsMultiArray = (ArrayCollection)(event.result);
 
            }
 
 
            private function faultHandler (event:FaultEvent):void {
             // Deal with event.fault.faultString, etc.
                Alert.show(event.fault.faultString, 'Error');
            }
 
        ]]>
    </mx:Script>
 
	<mx:DataGrid id="itemDetails" width="511" dataProvider="{itemsMultiArray}">
		<mx:columns>
			<mx:DataGridColumn headerText="ID" dataField="id"/>
			<mx:DataGridColumn headerText="Name" dataField="name"/>
			<mx:DataGridColumn headerText="Code" dataField="code"/>
		</mx:columns>
	</mx:DataGrid>
 
 
</mx:Application>

This is how it looks like:
flex_remote_object-1

The most important thing to note here is that the result that is got back in the call back function, should be cast with the proper data type in Flex. This is the link which has the details under the head Converting data from Java to ActionScript. From here we get to know the correct casting. In this case, we are casting the result into mx.collections.ArrayCollection since it is of java.util.List type.

Also note that though each element in the array is of the type Item, it can be accessed by its bean attribute name. That is how we can access the id, name and code in the DataGrid. Note that you cannot invoke the getter method like getId() on the object, it will not work.

So far, we have used a DataGrid which can extract data based on the attribute names. What if we use a ComboBox which needs a two dimensional array with data and label attributes? This is the code snippet of the modified getItemListResultHandler():

...
           [Bindable]
            private var itemComboArray:Array = new Array();
...
            private function getItemListResultHandler(event:ResultEvent):void {
 
                itemsMultiArray = (ArrayCollection)(event.result);
 
                var count:int = 0;
 
                for each (var item:Object in itemsMultiArray) {
                    var content:Object = new Object();
                    content["label"] = item.name;
                    content["data"] = item.id;
                    itemComboArray[count++] = content;
                } 
 
            }

And this is the code to add the ComboBox:

...
<mx:ComboBox id="itemCombo"  dataProvider="{itemComboArray}"></mx:ComboBox>
...

The screen looks like this now:
flex_remote_object-2

August 24, 2009

Integrating Spring (MVC) and BlazeDS (flex)

Filed under: flex, java, spring — Tags: , , , — paawak @ 22:33

Disclaimer:

I am not an expert in either Spring MVC or Flex and this article is not about fundamentals of Flex/BlazeDS. Its just how to get it working. Quickly.

Prologue:

I was playing around with Flex for the past couple of days. Being a Java developer, I was naturally inclined towards trying out BlazeDS so that I can use Flex as a front-end to my Java backend. The thing that I suffered from most was a huge information deluge. I was flooded by loads of blogs, articles and tutorials which made my life miserable. Finally, I saw a saviour in: Bare Bones BlazeDS Object Remoting, which explains how to create a BlazeDS and Java integration. Of course this does not include spring integration. Then I downloaded Spring BlazeDS, and went through the samples. Then I kind of combined these two and figured out how to make it work. The challenge here is that I have existing screens which are pure Spring MVC stuff. I am trying to embed flex in some of them, and have BlazeDS integration with my existing spring beans. Read on…

First things first: directory structure

My project directory looks like:

directory-structure1

Details:

  1. src/main/java contains all my java files
  2. src/main/resources has all config files like hibernate.cfg.xml, property files, etc.
  3. src/main/webapp has the WEB-INF folder and the jsps
  4. src/main/webapp/flash has all the compiled .swf files to be embedded in the jsps
  5. src/main/flex has all the .mxml and action script files

Config files required for BlazeDS

You would need these three config files under the WEB-INF/flex directory:

services-config.xml

This is the most important file. This defines the various channels that would be used for client/server communication by the BlazeDS

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
	<services>
		<service-include file-path="remoting-config.xml" />
		<default-channels>
			<channel ref="my-amf" />
		</default-channels>
	</services>
 
	<channels>
		<channel-definition id="my-amf"
			class="mx.messaging.channels.AMFChannel">
			<endpoint
				url="http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf"
				class="flex.messaging.endpoints.AMFEndpoint" />
			<properties>
				<polling-enabled>false</polling-enabled>
			</properties>
		</channel-definition>
	</channels>
 
	<logging>
		<!-- You may also use flex.messaging.log.ServletLogTarget -->
		<target class="flex.messaging.log.ConsoleTarget" level="info">
			<properties>
				<prefix>[BlazeDS] </prefix>
				<includeDate>false</includeDate>
				<includeTime>false</includeTime>
				<includeLevel>true</includeLevel>
				<includeCategory>false</includeCategory>
			</properties>
			<filters>
				<pattern>Endpoint.*</pattern>
				<pattern>Service.*</pattern>
				<pattern>Configuration</pattern>
			</filters>
		</target>
	</logging>
 
</services-config>

remoting-config.xml

This defines the remoting adapter used by the flex client.

<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
    class="flex.messaging.services.RemotingService">
    <adapters>
        <adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true"/>
    </adapters>
 
    <default-channels>
        <channel ref="my-amf"/>
    </default-channels>
 
  </service>

BlazeDS/Spring integration

To integrate Spring with BalzeDS, you have to touch upon these existing files:

web.xml

1. Adding the flex listener

Add the flex.messaging.HttpFlexSession listener.

2. URL mapping

Note that in the services-config.xml, the url of the default channel my-amf is http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf. You have to pass any url with this pattern to the Spring front end handler servlet, the org.springframework.web.servlet.DispatcherServlet. I assume that you already have a similar servlet defined for your Spring MVC. You should use the same servlet (so that you can re-use the same beans in flex) to map these urls. I am assuming the name of the existing DispatcherServlet is dispatcher.

This is how the modified web.xml looks like:

...
    <!-- *******************     START flex       **************************** -->
 
    <!-- Http Flex Session attribute and binding listener support -->
    <listener>
        <listener-class>flex.messaging.HttpFlexSession</listener-class>
    </listener>
 
    <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
 
    <!-- Map all *.spring requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/spring/messagebroker/*</url-pattern>
    </servlet-mapping>    
 
    <!-- *******************     END flex       **************************** -->
...

dispatcher-servlet.xml

This xml already has the existing Spring MVC beans. This has to be modified to expose existing beans to the BlazeDS remoting as services. The xml header has to be modified as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:flex="http://www.springframework.org/schema/flex"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/flex 
           http://www.springframework.org/schema/flex/spring-flex-1.0.xsd"
           default-lazy-init="true">
 
...

Then, the body has to be modified by adding:

...
    <!-- The default id of this bean is *_messageBroker*. It takes all config files by default.-->
    <flex:message-broker/>
    <bean class="org.springframework.flex.servlet.MessageBrokerHandlerAdapter"/>
 
    <!-- if this is not present, it gives a big exception -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!-- Maps all flex requests from blaze-ds to the flex message broker. You can re-use an existing bean instance -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <value>                                
                /messagebroker/*=_messageBroker
            </value>
        </property>
        <property name="order" value="0"/>
    </bean>
...

Finally, this is how you expose a bean as a service for remoting:

...
    <bean id="accountGroupManager" class="com.swayam.exp.AccountGroupManager"/>       
    <!-- Expose spring bean to blazeds for remoting. This will be available as a remote object with an id *account* -->
    <flex:remoting-destination ref="accountGroupManager" destination-id="account" />
...

This is how the AccountGroupManager looks like:

public class AccountGroupManager {
 
 
    public AccountGroupManager() {
 
        System.out
                .println("*****************AccountGroupManager.AccountGroupManager()");
    }
 
    public String save(String name, String desc) {        
// dummy code, returning static string
        return "40";
    }
 
 
}

Coding the mxml

I am using eclipse to code the NewAccountGroup.mxml. This is how it looks like:

<?xml version="1.0" encoding="UTF-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
 
<!-- Associate client with BlazeDS destination via RemoteObject. -->
<mx:RemoteObject id="accountGroupRemote" destination="account" />
 
    <mx:Form>
        <mx:FormItem label="Group Name:">
            <mx:TextInput id="groupName"/>
        </mx:FormItem>
 
        <mx:FormItem label="Description:">
            <mx:TextInput id="description"/>
        </mx:FormItem>
 
        <mx:FormItem>
            <mx:Button label="Submit" click="accountGroupRemote.save(groupName.text, description.text);" />
        </mx:FormItem>
 
        <mx:FormItem label="Server's Response">
            <mx:Label text="{accountGroupRemote.save.lastResult}" />
        </mx:FormItem>
 
    </mx:Form>
</mx:Application>

Compiling mxmls to swf

The one thing you have to keep in mind is that, you have to compile the mxmls along with the services-config.xml. Note that you have something like url=”http://{server.name}:{server.port}/{context.root}/spring/messagebroker/amf” in there. While the server.name and server.port will be taken by default by the client, you have to specify the context.root during the compile time, as an argument. This is the context of your webapp. I am using the mxmlc that comes with the Flex SDK from the command prompt (you can add it to the PATH). First you cd to the src/main/flex directory. Here you go:

mxmlc -services ../webapp/WEB-INF/flex/services-config.xml -strict=true -debug=true -context-root=/ -show-actionscript-warnings=true -output ../webapp/flash/NewAccountGroup.swf NewAccountGroup.mxml

Note how you specify the services-config.xml.

So far so good. Now we will have to embed the generated flash into our jsp. This how it is done:

...
<p>
 
    <object id="NewAccountGroup" align="middle"
    codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" 
    classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
    <param name="allowScriptAccess" value="sameDomain" />
    <param name="quality" value="high" />
    <param name="bgcolor" value="#ffffff" />
    <embed src="/flash/NewAccountGroup.swf" width="1000" height="400"
    quality="high" bgcolor="#ffffff" name="NewAccountGroup" align="middle" 
    allowScriptAccess="sameDomain" type="application/x-shockwave-flash" 
    pluginspage="http://www.macromedia.com/go/getflashplayer" />
    </object>
 
</p>
 
...

Finally, just a word for Maven users, these are the arifacts you have to use:

...
    <dependencies>
...
        <!-- *******************     START flex       **************************** -->
 
        <!-- blaze ds -->
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-common</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-core</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-opt</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-proxy</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
        <dependency>
            <groupId>com.adobe.blazeds</groupId>
            <artifactId>blazeds-remoting</artifactId>
            <version>${blazeds.version}</version>
        </dependency>
 
        <!-- spring-flex -->
        <dependency>
            <groupId>org.springframework.flex</groupId>
            <artifactId>spring-flex</artifactId>
            <version>${spring.flex.version}</version>
        </dependency>
 
        <dependency>
			<groupId>xalan</groupId>
			<artifactId>xalan</artifactId>
			<version>${xalan.version}</version>
        </dependency>
 
        <dependency>
            <groupId>edu.oswego.util</groupId>
            <artifactId>concurrent</artifactId>
            <version>${concurrent.version}</version>
        </dependency>
 
        <!-- *******************     END flex       **************************** -->
...        
    </dependencies>
...
    <properties>
        <blazeds.version>3.2.0.3978</blazeds.version>
        <spring.flex.version>1.0.0.RELEASE</spring.flex.version>
        <xalan.version>2.7.0</xalan.version>
        <concurrent.version>1.3.3</concurrent.version>
    </properties>
...

For others, you can always expand the blazeds.war and put all the jars inside the WEB-INF/lib.

The only pain point for this approach is that every time you change your context root, you have to recompile all your flex against the services-config.xml along with a new context.root argument.

This is how the end screen looks like (in the browser):

newaccountgroup

August 18, 2009

Virtualization with Sun Virtual Box: Tips n Tricks

Filed under: java — paawak @ 02:13

Guest Additions

These are to be done post installation. It improves usability. For example, it provides seamless mouse integration. That is, you do not have to press the Host Key (normally the Right CTRL) every time you move your mouse back and forth between the host and the guest. Also, it improves the full screen mode. Even before installing this, my shared folders were not detected on my Windows guest on a Linux host.

This is how you do it, go to Devices -> Install Guest Additions as shown below:

windowsguestonfedora

You will find the details in the online tutorial here.

August 11, 2009

Error in named query: org.hibernate.hql.ast.QuerySyntaxException: table is not mapped

Filed under: database, hibernate, java — Tags: — paawak @ 02:29

I was taking advantage of the NamedQueries feature in Hibernate annotation, when suddenly I was bombed with this queer error:

ERROR - SessionFactoryImpl.<init>(363) | Error in named query: findMaxId
org.hibernate.hql.ast.QuerySyntaxException: AccountGroup is not mapped [SELECT MAX(id) FROM AccountGroup]

My AccountGroup looks like this:

@Entity(name = "account_group")
@NamedQueries( { @NamedQuery(name = AccountGroup.NAMED_QUERY_FIND_MAX_ID, query = "SELECT MAX(acg.id) FROM AccountGroup acg") })
public class AccountGroup implements Serializable {
 
    private static final long serialVersionUID = 8651011772980546778L;
 
    public static final String NAMED_QUERY_FIND_MAX_ID = "findMaxId";
...
 
}

As is evident from the error message, the HQL compiler is not able to map any table to “AccountGroup”. But this is strange, since I have already mapped it in my hibernate.cfg.xml. After a few minutes of futile googling, the solution dawned upon me.

Solution 1:

Include an additional annotation @Table:

@Entity
@Table(name = "account_group")
@NamedQueries( { @NamedQuery(name = AccountGroup.NAMED_QUERY_FIND_MAX_ID, query = "SELECT MAX(acg.id) FROM AccountGroup acg") })
public class AccountGroup implements Serializable {
 
    private static final long serialVersionUID = 8651011772980546778L;
 
    public static final String NAMED_QUERY_FIND_MAX_ID = "findMaxId";
...
 
}

Solution 2:

Modify your query to have the fully qualified class name:

@Entity(name = "account_group")
@NamedQueries( { @NamedQuery(name = AccountGroup.NAMED_QUERY_FIND_MAX_ID, query = "SELECT MAX(acg.id) FROM com.swayam.test.AccountGroup acg") })
public class AccountGroup implements Serializable {
 
    private static final long serialVersionUID = 8651011772980546778L;
 
    public static final String NAMED_QUERY_FIND_MAX_ID = "findMaxId";
...
 
}

June 30, 2009

Recursively deleting a row in a table having Foreign Key Constraints

Filed under: database, java — Tags: — paawak @ 23:32

An oft encountered and irritating scenario while working with a database is when we come across a certain row which we want to delete, but cannot as it has recursive Foreign Key Constraints. Today I got a particularly nasty scenario as we are using Teneo. It has around four levels of Foreign Key Constraints. So, I decided to write this utility so that given a table name, column name and value, it deletes the row and all the Foreign Key Constrained data in order. I do this by making sense of the error message that I get when trying to delete a row with Foreign Key Constraints.

It looks something like this with MySQL:

Cannot delete or update a parent row: a foreign key constraint fails (`some_database/user`, CONSTRAINT `my_constraint` FOREIGN KEY (`id`) REFERENCES `employee`  (`id`))

To deal with this, I have defined this interface to extract meaningful information from the error message, so that I can delete the rogue row of the rogue table first.

package com.swayam.db.tools;
 
/**
 *@author paawak
 */
public interface ConstraintViolationExceptionInterpretor {
 
    void setExceptionMessage(String message);
 
    String getConstrainedTableName();
 
    String getForeignKeyColumnName();
 
    String getReferencedTableName();
 
    String getReferencedColumnName();
 
}

This is the implemenetation for MySQL:

package com.swayam.db.tools;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
/**
 *
 * @author paawak
 */
public class MySQLConstraintViolationExceptionInterpretor implements
        ConstraintViolationExceptionInterpretor {
 
    private String message;
 
    public MySQLConstraintViolationExceptionInterpretor() {
 
    }
 
    public String getConstrainedTableName() {
 
        String match = getFirstMatch("/\w+");
 
        if (match != null) {
            match = match.substring(1);
        }
 
        return match;
 
    }
 
    public String getForeignKeyColumnName() {
 
        String match = getFirstMatch("\(`\w+`\)");
 
        if (match != null) {
            match = match.substring(2, match.length() - 2);
        }
 
        return match;
 
    }
 
    public String getReferencedColumnName() {
 
        String match = getFirstMatch("\(`\w+`\)\)");
 
        if (match != null) {
            match = match.substring(2, match.length() - 3);
        }
 
        return match;
 
    }
 
    public String getReferencedTableName() {
 
        String prefix = "REFERENCES `";
 
        String match = getFirstMatch(prefix + "\w+`");
 
        if (match != null) {
            match = match.substring(prefix.length(), match.length() - 1);
        }
 
        return match;
 
    }
 
    public void setExceptionMessage(String message) {
        this.message = message;
    }
 
    private String getFirstMatch(String regex) {
 
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(message);
 
        if (matcher.find()) {
            String match = matcher.group();
            return match;
        }
 
        return null;
 
    }
 
}

And finally the class that does the dirty job of recursively executing all the deletes:

package com.swayam.db.tools;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
 
/**
 *@author paawak
 */
public class RecursiveDeleteUtil {
 
    private final Connection con;
    private final List deleteStatements;
    private final ConstraintViolationExceptionInterpretor interpretor;
    private String idColumn;
    private String idValue;
 
    public RecursiveDeleteUtil(Connection con,
            ConstraintViolationExceptionInterpretor interpretor) {
        this.con = con;
        this.interpretor = interpretor;
        deleteStatements = new ArrayList(1);
    }
 
    public void delete(String tableName, String column, String value) {
 
        if (idColumn == null) {
            idColumn = column;
            idValue = value;
        }
 
        String delQuery = "DELETE FROM " + tableName + " WHERE " + column
                + "='" + value + "'";
 
        deleteStatements.add(delQuery);
 
        try {
 
            Statement stat = con.createStatement();
            int rowsAffected = stat.executeUpdate(delQuery);
 
            if (rowsAffected > 0) {
 
                // execute all stats on the stack
                int lastIndex = deleteStatements.size() - 1;
 
                for (int count = lastIndex; count >= 0; count--) {
                    stat.executeUpdate(deleteStatements.get(count));
                }
 
            }
 
            stat.close();
 
        } catch (SQLException e) {
 
            String message = e.getMessage();
            interpretor.setExceptionMessage(message);
            String constrainedTableName = interpretor.getConstrainedTableName();
            String foreignKeyColumnName = interpretor.getForeignKeyColumnName();
            String referencedTableName = interpretor.getReferencedTableName();
            String referencedColumnName = interpretor.getReferencedColumnName();
            System.out.println("################# message = " + message
                    + "nconstrainedTableName = " + constrainedTableName
                    + ", foreignKeyColumnName = " + foreignKeyColumnName
                    + ", referencedTableName = " + referencedTableName
                    + ", referencedColumnName = " + referencedColumnName);
 
            // select the offending value
            String sel = "SELECT " + referencedColumnName + " FROM "
                    + referencedTableName + " WHERE " + idColumn + "='"
                    + idValue + "'";
 
            System.out.println("################ select = " + sel);
 
            try {
                Statement selectStat = con.createStatement();
                ResultSet res = selectStat.executeQuery(sel);
 
                while (res.next()) {
                    String constraintVal = res.getString(1);
                    delete(constrainedTableName, foreignKeyColumnName,
                            constraintVal);
                }
 
                res.close();
                selectStat.close();
 
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
 
        }
 
    }
 
    public static void main(String[] args) throws Exception {
 
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        String conStr = "jdbc:mysql://localhost/dummy?user=root&password=dummy";
 
        Connection con = DriverManager.getConnection(conStr);
 
        new RecursiveDeleteUtil(con,
                new MySQLConstraintViolationExceptionInterpretor()).delete(
                "some_table", "id", "118");
 
    }
 
}

I don’t claim this to be very generic, but it works pretty well for me :). A great time saver for sure.

June 18, 2009

Saving EMF to XML

Filed under: emf — Tags: , — paawak @ 00:58

Lets say we want to serialize an EObject to XML. The code is given below. This will print the XML to the error stream.

 
            EObject eObject = ...;
 
            Resource resource = new XMLResourceImpl();
            resource.getContents().add(eObject);
 
            Map options = new HashMap();
            options.put(XMLResource.OPTION_ENCODING, "UTF-8");
            options.put(XMLResource.OPTION_XML_VERSION, "1.0");      
            new XMLProcessor().save(System.err, resource, options);
« Newer PostsOlder Posts »

Powered by WordPress