Tremend Tech Blog

"Software is a great combination between artistry and engineering. When you finally get done and get to appreciate what you have done it is like a part of yourself that you've put together." (Bill Gates)

Looking for software experts?

Need an expert advice on software development? Need consulting work done in time and at high standards? Tremend has the right solution for you.

We can provide expertise in:
  • high traffic and complex content website infrastructures
  • website development-advanced web programming with PHP, .NET, Java, Flash/Flex, Ajax

Our friends

Springy: org aspectj weaver reflect ReflectionWorld ReflectionWorldException: warning can’t determine implemented interfaces of missing type

March 30th, 2009 by Marius Hanganu

Here’s a quick one: if you’ve encountered the following error

1
2
3
4
5
org.springframework.beans.factory.BeanCreationException: Error creating bean with name '...': Initialization of bean failed; nested exception is org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException: warning can't determine implemented interfaces of missing type ...
 [Xlint:cantFindType]
Caused by: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException: warning can't determine implemented interfaces of missing type ro.tremend.bigg.web.tile.currencyCustomTileRenderer2
 [Xlint:cantFindType]
	at org.aspectj.weaver.reflect.ReflectionWorld$ExceptionBasedMessageHandler.handleMessage(ReflectionWorld.java:163)

And didn’t figure out yet what was the problem, you may want to check your spring version. This error happens in 2.0.2  but it seems it was fixed since 2.0.5.

More here

Share/Save

Posted in Java, General, Spring | No Comments »

Dynamically create Spring beans from groovy scripts at runtime

February 24th, 2009 by Marius Hanganu

Groovy

In the current project - a java based CMS - we were faced with an interesting problem: how can we dynamically create renderers to power our blocks in the html page (currently all renderers are Java classes and Spring beans also described in the application context files).

Imagine the power of this approach. Especially when Java deployments can be quite a hassle in a multi server environment (deploying sources on the linux machines, wait for a good moment for starting/stopping the servers - which may or may not perform a sensitive operation at that time, synchronize the restart of the web servers, check log files for any errors, etc).

What could be better than having your Spring beans dinamically created from the administration panel? No server restarts, no deployments. Just keep your Groovy scripts in the database and make any modification realtime.

Achieving the programatic creation of Spring beans from groovy scripts can be done in four steps:

1. load the groovy script and parse it into a java Class
2. create a bean definition using the previously loaded class
3. inject any other beans into your bean definition
4. register the bean definition and get a handle on the bean created

Of course, you cannot declare variables as instances of the Java class defined in the groovy script. Or you may do it programatically through reflection. But it is much simpler (if the project suits these needs) to have a base interface for all your dynamic beans and after those 4 steps, simply cast the bean created to the Java interface. So you might add two more steps to the previous four:

0. declare a base interface for your all your classes declared in groovy scripts
5. cast the created bean to the base interface so that you can use it in your code

The code is actually pretty simple. But you can hardly find any article/post on the subject. Most of them discuss an older version of Spring (1.x), while my code had to work with Spring 2.0.x - which is completely different in terms of the API calls needed to create a bean.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class GroovyTemplateEngineImpl extends DefaultListableBeanFactory implements ApplicationContextAware {
    public BaseInterface createOrUpdateBean(String rendererName, String code) throws Exception, ClassNotFoundException {
 
        // 1. load the groovy script and parse it into a class
        GroovyClassLoader gcl = new GroovyClassLoader(getClassLoader());
        Class clazz = gcl.parseClass(groovyScriptAsAString);
 
        // 2. create the bean definition
        AbstractBeanDefinition beanDef = BeanDefinitionReaderUtils.createBeanDefinition( "parentBean", packageName + "." + className, gcl);
 
        // 3. inject here any attributes that would normally be passed using spring XML configuration files
        beanDef.setAttribute("attr1", bean1);
        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) appCtx.getAutowireCapableBeanFactory();
 
        // 4. Create the bean - I'm using the class name as the bean name
        factory.registerBeanDefinition(className, beanDef);
        Object bean = factory.createBean(clazz, AUTOWIRE_BY_NAME, false);
 
        // 5. further on you can cast it to any interface
        return (BaseInterface) bean;
    }
}

You will also need to add to the previous class a setter for application context to be injected (the class needs to implement ApplicationContextAware).

Please note that this code only works from spring version 2.0.3 and up. If you’re using a lower version, you’ll likely encounter this exception:

1
2
3
Caused by: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException: 
warning can't determine implemented interfaces of missing type .... 
[Xlint:cantFindType]

Good luck groovying!

Share/Save

Posted in Java, General | 1 Comment »

How to order by a custom SQL formula/expression when using hibernate Criteria API

June 10th, 2008 by spostelnicu

In our current project we are using Spring + Hibernate Annotations.
Today I needed to use something like:

1
2
3
SELECT DISTINCT t.id FROM MyClass t
 WHERE .....
 ORDER BY (a + b) DESC

where a and b are properties of MyClass (columns in the “my_class” table).

The “where …” expression must be generated dynamically based on the user input, so we are using Criteria API to generate the query as such:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Criteria criteria = getSession().createCriteria(MyClass.class);
criteria.setProjection(Projections.distinct(Projections.id()));
 
// Some custom dynamic conditions
criteria.add(Restrictions.gt("createdDate", afterDate));
criteria.add(Restrictions.in("state", approvedStates));
criteria.add(Restrictions.isNull("deletedDate"));
if (includedCategories != null || excludedCategories != null) {
    Criteria categoryCriteria = criteria.createCriteria("category");
    if (includedCategories != null) {
        for (String categoryPrefix : includedCategories) {
            categoryCriteria.add(Restrictions.like("path", categoryPrefix + "%"));
        }
    }
    if (excludedCategories != null) {
        for (String categoryPrefix : excludedCategories) {
            categoryCriteria.add(Restrictions.not(Restrictions.like("path", categoryPrefix + "%")));
        }
    }
}
 
criteria.add(Restrictions.sqlRestriction("(a + b) > 1"));
 
// Custom ordering by some SQL formula/expression
criteria.addOrder(Order.desc("a + b"));
 
return criteria.list();

Now, the problem is that the class Order does not support custom SQL formula/expression…

So my solution was to derive my own class as such:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package ro.tremend.util.hibernate;
 
import org.hibernate.criterion.Order;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
 
/**
 * Extends {@link org.hibernate.criterion.Order} to allow ordering by an SQL formula passed by the user.
 * Is simply appends the <code>sqlFormula</code> passed by the user to the resulting SQL query, without any verification.
 * @author Sorin Postelnicu
 * @since Jun 10, 2008
 */
public class OrderBySqlFormula extends Order {
    private String sqlFormula;
 
    /**
     * Constructor for Order.
     * @param sqlFormula an SQL formula that will be appended to the resulting SQL query
     */
    protected OrderBySqlFormula(String sqlFormula) {
        super(sqlFormula, true);
        this.sqlFormula = sqlFormula;
    }
 
    public String toString() {
        return sqlFormula;
    }
 
    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
        return sqlFormula;
    }
 
    /**
     * Custom order
     *
     * @param sqlFormula an SQL formula that will be appended to the resulting SQL query
     * @return Order
     */
    public static Order sqlFormula(String sqlFormula) {
        return new OrderBySqlFormula(sqlFormula);
    }
}

Now, to use the custom ordering, I included the following line:

1
        criteria.addOrder(OrderBySqlFormula.sqlFormula("(a + b) desc"));

Share/Save

Posted in Java, General | No Comments »

How to use the request Referer as the targetUrl when using Acegi Security for Spring

September 7th, 2007 by spostelnicu

Suppose that my login form is integrated in another page and I want to return to that page that integrated my login page (so the original page).
How do you do that in ACEGI ?

My solution was to extend the AuthenticationProcessingFilter and add a property named useRefererAsTargetUrl.
If this property is set to true, then the AuthenticationProcessingFilter will redirect to the value of the request Referer header upon successful authentication, unless the targetUrl can be taken from a SavedRequest, (which usually means that the authentication request was caused by an AccessDeniedException or AuthenticationException thrown within the filter chain). If alwaysUseDefaultTargetUrl is also set to true, then the defaultTargetUrl will be used, and this flag will be ignored.
This flag defaults to false, which is the default behaviour of AuthenticationProcessingFilter.

To set it to true, simply use the following Spring configuration:

1
2
3
4
5
6
7
8
<bean id="authenticationProcessingFilter" class="ro.tremend.web.filters.ExtendedAuthenticationProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="authenticationFailureUrl" value="/#invalid_login"/>
    <property name="defaultTargetUrl" value="/"/>
    <property name="useRefererAsTargetUrl" value="true"/>
    <property name="filterProcessesUrl" value="/web_login_check"/>
    <property name="rememberMeServices" ref="rememberMeServices"/>
</bean>

The source code for the class is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class ExtendedAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
 
    /**
     * If <code>true</code>, will redirect to the value of the request Referer header upon successful authentication,
     * unless the targetUrl can be taken from a {@link org.acegisecurity.ui.savedrequest.SavedRequest}
     * (which usually means that the authentication request was caused by an
     * <code>AccessDeniedException</code> or <code>AuthenticationException</code> thrown within the filter chain).
     * If alwaysUseDefaultTargetUrl is also set to true, then the defaultTargetUrl will be used,
     * and this flag will be ignored.
     * This flag defaults to <code>false</code>, which is the default behaviour of AuthenticationProcessingFilter.
     */
    private boolean useRefererAsTargetUrl = false;
 
    protected String determineTargetUrl(HttpServletRequest request) {
        // Don't attempt to obtain the url from the saved request if
        // alwaysUsedefaultTargetUrl is set
        if (isAlwaysUseDefaultTargetUrl()) {
            return getDefaultTargetUrl();
        }
 
        String targetUrl = obtainFullRequestUrl(request);
 
        if (targetUrl == null && useRefererAsTargetUrl) {
            targetUrl = obtainRequestRefererUrl(request);
        }
 
        if (targetUrl == null) {
            targetUrl = getDefaultTargetUrl();
        }
 
        return targetUrl;
    }
 
    public static String obtainRequestRefererUrl(HttpServletRequest request) {
        return request.getHeader(HttpUtils.HTTP_HEADER_REFERER);
    }
 
    public boolean isUseRefererAsTargetUrl() {
        return useRefererAsTargetUrl;
    }
 
    public void setUseRefererAsTargetUrl(boolean useRefererAsTargetUrl) {
        this.useRefererAsTargetUrl = useRefererAsTargetUrl;
    }
}

Maybe (in a future release of Acegi Security) this feature will simply be integrated in AbstractProcessingFilter, where it belongs…

Share/Save

Posted in Java, General | No Comments »

Create a Solr filter that replaces diacritics

August 28th, 2007 by Sebastian Mitroi

Some languages (like Romanian) have special characters (diacritics, often called accent marks). It’s generally useful to remove diacritic marks from characters, for example when you create an index with Solr. You don’t want to index text with these characters because you want to find for example both words “proprietăţi” and “proprietati”. If you are using Solr to index your text you have to create a Solr filter.
First of all you have to put the filter in the schema.xml configuration file :


<fieldtype name="text_st" class="solr.TextField" positionIncrementGap="100">
            <analyzer>
                <tokenizer class="solr.StandardTokenizerFactory"/>
                // ... some other filters for example lower case filter
                <filter class="solr.LowerCaseFilterFactory"/>   
                <filter class="ro.tremend.solr.diacritics.DiacriticsFilterFactory"/>

            </analyzer>
</fieldtype>

Then create 3 small classes and a properties file. The filter factory for Solr DiacriticsFilterFactory :

package ro.tremend.solr.diacritics;

import org.apache.lucene.analysis.TokenStream;
import org.apache.solr.analysis.BaseTokenFilterFactory;

/**
 * Create a Solr Filter Factory for diacritics
 *
 * @author Sebastian
 *
 */
public class DiacriticsFilterFactory extends BaseTokenFilterFactory {
	public TokenStream create(TokenStream input) {
		return new DiacriticsFilter(input);
	}
}

Now you have to create the filter class DiacriticsFilter :

package ro.tremend.solr.diacritics;

import org.apache.lucene.analysis.*;
import java.io.IOException;

/**
 * Create the diacritics filter
 *
 * @author Sebastian
 *
 */
public final class DiacriticsFilter extends TokenFilter {
	public DiacriticsFilter(TokenStream in) {
		super(in);
	}

	public final Token next() throws IOException {
		Token t = input.next();

		if (t == null)
			return null;

		t.setTermText(DiacriticsUtils.replaceDiacritics(t.termText()));
		return t;
	}
}

and finally the class that does the work DiacriticsUtils :

package ro.tremend.solr.diacritics;

import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;

/**
 * Replace romanian characters
 *
 * @author Sebastian
 *
 */
public class DiacriticsUtils {
	private static Map diacritics = new HashMap();

	static {
		// Get diacritics from diacritics.properties
		try {
			ResourceBundle resource = ResourceBundle.getBundle("diacritics");
			Set keySet = resource.keySet();
			for (String key : keySet) {
				diacritics.put(key, resource.getString(key));
			}
		} catch (MissingResourceException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Replace all diacritics in a string
	 *
	 * @param s the string
	 * @return the string without diacritics
	 */
	public static String replaceDiacritics(String s) {
		for (String key : diacritics.keySet()) {
			s = s.replaceAll(key, diacritics.get(key));
		}
		return s;
	}

	public static Map getDiacritics() {
		return diacritics;
	}
}

This class needs a properties file with the diacritics you want to replace:
diacritics.properties

\\u0102=A
\\u0103=a
... define all your language specific characters


Now the index will not contain diacritics, but you have to remove the diacritics from the query too. To do that just write this:

textToFind = DiacriticsUtils.replaceDiacritics(textToFind);


I hope this will help.

Share/Save

Posted in Java, General | 7 Comments »

Avoiding SQL joins with java enums

August 28th, 2007 by Sebastian Mitroi

Lets say you have a Coffee object and 3 sizes for coffee (small, medium and large).You can create a Coffee class like this

public class Coffee {
	private Long id;
	// add other propeties
	private CoffeeSize coffeeSize;

	// ... setters and getters
}	

and the CoffeeSize class:

public class CoffeeSize {
	private int id;
	private String name;
	private String i18nKey;

	// ... setters and getters
}

But every time you load a Coffee object you execute a sql join to load the CoffeeSize.

Of course you can create a CoffeeSize object and define some public static final CoffeeSize objects, but with java 1.5 you can do something like this :
create a java enum named CoffeeSize and persists just the CoffeeSize id.

package coffee;

import java.util.HashMap;
import java.util.Map;

public enum CoffeeSize {
	SMALL(1, "Small", "coffeeSize.small"),
	MEDIUM(1, "Medium", "coffeeSize.medium"),
	LARGE(1, "Large", "coffeeSize.large");

	private int id;
	private String name;
	private String i18nKey;

	private static Map coffeeSizes = new HashMap();

	static{
		CoffeeSize[] coffeeSizesArray = CoffeeSize.values();
		for (CoffeeSize coffeeSize : coffeeSizesArray) {
			coffeeSizes.put(coffeeSize.getId(), coffeeSize);
		}
	}

	private CoffeeSize(int id, String name, String key) {
		this.id = id;
		this.name = name;
		i18nKey = key;
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	/**
	 * This is an i18n key defined in message.properties
	 * @return the i18n key
	 */
	public String getI18nKey() {
		return i18nKey;
	}

	/**
	 * For the id stored in database get the CoffeeSize object
	 * @param id the id stored in database
	 * @return the {@link CoffeeSize} object
	 */
	public static CoffeeSize getCoffeeSizeById(Integer id) {
		return CoffeeSize.coffeeSizes.get(id);
	}
}

The Coffee object will look something like this (I used hibernate and ejb3 annotations):

package coffee;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

@Entity
@Table(name = "coffee")
public class Coffee {
	private Long id;
	// add other propeties
	private Integer coffeeSizeId;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public Integer getCoffeeSizeId() {
		return coffeeSizeId;
	}

	public void setCoffeeSizeId(Integer coffeeSizeId) {
		this.coffeeSizeId = coffeeSizeId;
	}

	@Transient
	public CoffeeSize getCoffeeSize(){
		return CoffeeSize.getCoffeeSizeById(coffeeSizeId);
	}
}


Look at the getCoffeeSize method: it is transient(is not persisted). The persisted fields are id and coffeeSizeId. You avoid a sql join putting private Integer coffeeSizeId instead private CoffeeSize coffeeSize and create a getCoffeeSize method that returns a CoffeeSize object. All the CoffeeSize objects are loaded in memory in the static map coffeeSizes.

But remember, this will work only if you don’t want to add a new size for coffee without recompiling and deploying your application.

Share/Save

Posted in Java, General | No Comments »

Hibernate annotations - default value

August 27th, 2007 by Marius Hanganu

If you wanted to set the default value using hibernate annotations, you’ve probably had some difficulties, as it was the case for me. Some posts on the web talk about default values to the members of the Java class. That is, if you declare

class Test {

    private Integer count = 3;

    @Column(name = “count”, nullable = false)

    public Long getCount() {

        return Count;

    }

}

you should have the default value set in the database.

Well, this does not seem to work (at least not for me). So the first solution I found involves the usage of “columndefinition”. Hence, it is database dependent, since Hibernate specifies the usage of “columndefinition” attribute for database specific declarations. The following will work well with MySQL - the database of choice for my current project:


class Test {

    private Integer count = 3;

    @Column(name = “count”, nullable = false, columnDefinition = “bigint(20) default 0″)

    public Long getCount() {

        return Count;

    }

}

Again - this is database dependent, so use it if your project is db dependent.

Share/Save

Posted in Java, General | 6 Comments »

How to set the default charset to utf-8 for create table when using hibernate with java persistence annotations

August 14th, 2007 by spostelnicu

Yesterday I encountered a problem when trying to persist a String value into a MySQL column of type ‘text‘ (the problem also occurs for column types ‘tinytext‘, ‘mediumtext‘ etc.)

The first confusing thing was that the error message returned by mysql was
java.sql.BatchUpdateException: Data truncation: Data too long for column 'my_column'

The confusing thing was that the column was of type ‘text‘ (length=65536), and I then changed it to type ‘mediumtext‘ (length=16777215), but the value that I was trying to persist had only around 6000 characters, so it was not a problem with the data length.
Instead the one thing that was noticeable about my String value was that it contained non-ASCII characters (particularly Romanian characters with diacritics).

After a little search on the web, I found the following mysql bug description http://bugs.mysql.com/bug.php?id=17872
A comment on that page ([8 Mar 2006 9:53] [ name withheld ]) also contains some more links to similar bug descriptions.

Now it was a little clearer that the problem was caused by the Romanian characters (and the error message was just stupid), so the next thing to do was to figure out why the database table didn’t store UTF-8 characters.

Although I created the database with
create database mydb character set utf8 collate utf8_general_ci;
still the tables created inside it had the collation latin1_swedish_ci.

My application uses Hibernate for persistence and uses java persistence annotations to specify the hibernate mappings.

In my case the code is something like:


import javax.persistence.*;

@Entity
@Table(name = "my_table")
public class MyClass implements Serializable {
    private String myValue;

    @Column(name = "my_column", columnDefinition = "mediumtext", length = 16777215)
    public String getMyValue() {
        return this.myValue;
    }

    public void setMyValue(String value) {
        this.myValue = value;
    }
}

The database schema is automatically generated based on the classes and hibernate mappings, by using the following ant target:


    <taskdef name="hibernatetool"
             classname="org.hibernate.tool.ant.HibernateToolTask"
             classpathref="project.classpath"/>

    <target name="hbm2ddl-schema"
            description="Generates the database schema from hibernate mappings">
        <hibernatetool destdir="">
            <classpath refid="project.classpath"/>
            <annotationconfiguration configurationfile="${classes.dir}/hibernate.cfg.xml"/>
            <hbm2ddl export="true" drop="false" create="true" haltonerror="true"/>
        </hibernatetool>
    </target>

If I had created the database tables by writing the SQL DDL by hand, I would have used the following script:


  create table `my_table` (
      `Id` int(11) NOT NULL auto_increment,
      `my_column` mediumtext NOT NULL default ”,
      ......
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

But in my case the database schema is automatically generated by HibernateTools, so I have to specify the default charset for the table somewhere in the @Table annotation, or hibernate configuration, or hibernatetool parameters.

After trying a few options (and I must admit I did not search thoroughly, so if you know of some better way to specify the default charset, please leave a comment), I chose the following solution:
I implemented my own custom org.hibernate.dialect.Dialect, by subclassing org.hibernate.dialect.MySQL5InnoDBDialect:


import org.hibernate.dialect.MySQL5InnoDBDialect;

/**
 * Extends MySQL5InnoDBDialect and sets the default charset to be UTF-8
 * @author Sorin Postelnicu
 * @since Aug 13, 2007
 */
public class CustomMysqlDialect extends MySQL5InnoDBDialect {

    public String getTableTypeString() {
        return " ENGINE=InnoDB DEFAULT CHARSET=utf8";
    }
}

and used it in my hibernate.cfg.xml:


<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">my.package.CustomMysqlDialect</property>
        .....
    </session-factory>
</hibernate-configuration>

Share/Save

Posted in Java, General | 7 Comments »

Hibernate schema maintenance

July 31st, 2007 by Ioan Cocan

Hibernate provides a nice way to keep your DB schema in sync with the model. At least 2 choices are available:

- have the hibernate.hbm2ddl.auto set to auto during development

- use various utilities: SchemaExportTask or HibernateToolTask(newer) to generate the full DB schema

For development, first choice worked except for one thing: index creation. All columns were properly created but no index defined. As I found no workaround to that, the only solution was to generate the whole schema.

What if the Hibernate model mixed hbm.xml files with annotations (reused components, mostly)? The SchemaExportTask or HibernateToolTask do not handle the mixed model not to mention the integration with Spring and the missing hibernate.cfg.xml file required by HibernateToolTask.

Only one solution: go to the code and generate the schema by calling the code directly. Easy task actually, I’ve placed the code in a quick Junit test with Spring auto-injection:

/**
 * Maintains DB schema
 *
 * @author icocan
 */
public class SchemaMaintenanceTest extends BaseCommitTestCase {

    private LocalSessionFactoryBean localSessionFactoryBean;

    public void testExportDDL() {
        Configuration configuration = localSessionFactoryBean.getConfiguration();
        SchemaExport export = new SchemaExport(configuration);
        export.setOutputFile("sql.ddl");
        export.setDelimiter(";");
        export.execute(false, false, false, false);
    }

    public void setLocalSessionFactoryBean(LocalSessionFactoryBean localSessionFactoryBean) {
        this.localSessionFactoryBean = localSessionFactoryBean;
    }
}

Share/Save

Posted in Java, General | 1 Comment »

DWR, JSON and IE6: beware keywords

July 20th, 2007 by Ioan Cocan

Spent quite a bit of time investigating an “IE bug”. What happened: a particular AJAX request would work fine in Firefox but nothing would happen in IE6. Obvious steps were to monitor request and response (all fine), JS imports in files (all fine).

The final explanation was quite simple: that particular object (Customer), contained a field called “function”. The JSON string returned by DWR was correctly interpreted by FF but would make IE6 hang. The simple solution: rename the field and beware any other JS keywords.

Share/Save

Posted in Java, General | 1 Comment »

Hibernate, UTF-8 and SQL Server 2005

May 23rd, 2007 by Ioan Cocan

I found out today that MS Sql server seems to handle Unicode in a very special way. Instead of having some support a database or table level, each Unicode column have to be created as “national”. That is be either nchar, nvarchar or ntext.

Ms SQL Server 2005 seems to go one step further by announcing future deprecation for ntext, text and image types.

From Sql Server 2005 notes:

ntext, text, and image data types will be removed in a future version of Microsoft SQL Server. Avoid using these data types in new development work, and plan to modify applications that currently use them. Use nvarchar(max), varchar(max), and varbinary(max) instead.”

When working with Hibernate it seems there is no dialect to handle Unicode integration properly. You have to get down and write a custom dialect that maps to the new data types.

/**
 * Unicode support in SQL Server
 *
 * @author icocan
 */
public class UnicodeSQLServerDialect extends SQLServerDialect {

    public UnicodeSQLServerDialect() {
        super();

        // Use Unicode Characters
        registerColumnType(Types.VARCHAR, 255, "nvarchar($l)");
        registerColumnType(Types.CHAR, "nchar(1)");
        registerColumnType(Types.CLOB, "nvarchar(max)");

        // Microsoft SQL Server 2000 supports bigint and bit
        registerColumnType(Types.BIGINT, "bigint");
        registerColumnType(Types.BIT, "bit");
    }
}

Share/Save

Posted in Java, General | 6 Comments »

A-Z 0-9 custom sorting in Lucene

May 17th, 2007 by Ioan Cocan

The natural sort order for String fields is 0-9 A-Z so it seems a custom sorter is needed. I guess this is a normal request for any application that does a little sorting so I am surprised nothing came up during a search. Here is the AZ09Comparator:

/**
 * Custom Lucene sorting A-Z 0-9
 *
 * @author icocan
 */
public class AZ09Comparator implements SortComparatorSource {

    private static final Logger logger = Logger.getLogger(AZ09Comparator.class);

    public ScoreDocComparator newComparator(final IndexReader indexReader, final String str) throws IOException {
        return new ScoreDocComparator() {
            public int compare(ScoreDoc scoreDoc1, ScoreDoc scoreDoc2) {
                try {
                    final Document doc1 = indexReader.document(scoreDoc1.doc);
                    final Document doc2 = indexReader.document(scoreDoc2.doc);
                    final String strVal1 = doc1.get(str);
                    final String strVal2 = doc2.get(str);

                    boolean c1 = Character.isDigit(strVal1.charAt(0));
                    boolean c2 = Character.isDigit(strVal2.charAt(0));

                    if (c1 && c2) {
                        return strVal1.compareTo(strVal2);
                    } else if (c1) {
                        return 1;
                    } else if (c2) {
                        return -1;
                    } else {
                        return strVal1.compareTo(strVal2);
                    }
                } catch (IOException e) {
                    logger.error("Cannot read doc", e);
                }
                return 0;
            }

            public Comparable sortValue(ScoreDoc scoreDoc) {
                return new Float(scoreDoc.doc);
            }

            public int sortType() {
                return SortField.CUSTOM;
            }
        };
    }
}

Share/Save

Posted in Java, General | 1 Comment »

Problem when adding elements to a TreeSet<Comparator> - some elements are not added

May 17th, 2007 by spostelnicu

I thought of sharing with you this little problem that I encountered when using a TreeSet to display an ordered list of items:

I have some items stored in the database, each of them having a unique id.

Some of the items have the same values, only the ids are different.

I also have a business method that creates a list of items, sorted by some custom ordering of fields.

To sort them, I used the following lines of code:

SortedSet result = new TreeSet(itemsComparator);
for (Item item : someItems) {
    if (itemIsEligible(item)) {
        result.add(item);
    }
}

where itemsComparator is an instance of this class:

public class ItemsComparator implements Comparator {
    /**
     * Compare two items for order.
     * @param item1 the first item
     * @param item2 the second item
     * @return -1 if item1 should appear before item2,
               1 if item2 should appear before item1,
               or 0 if the order is not important
     */
    public int compare(Item item1, Item item2) {
        // Sort by field1 descending
        int compareField1 = item1.getField1().compareTo(item2.getField1());
        if (compareField1 != 0) {
            return -compareField1;
        }
        // For equal field1, sort by field2 ascending
        int compareField2 = item1.getField2().compareTo(item2.getField2());
        return compareField2;
    }
}

At some moment I discovered the following bug: if two items had the same values, but different ids, only the last one of them appeared in the result.

After a little debugging I discovered that the problem was this:

TreeSet uses internally a TreeMap to store the elements.

TreeMap uses compareTo to determine the order of the elements, to know where to place them in the internal tree.

And if two elements are different but compareTo returns 0, then TreeMap will consider them equal, and the newly added element replaces the old one.

My solution to this problem was the following:

public class ItemsComparator implements Comparator {
    /**
     * Compare two items for order.
     * To ensure that two different items are not treated as equal,
     * our method will never return 0 for different items!
     * @param item1 the first item
     * @param item2 the second item
     * @return -1 if item1 should appear before item2,
                    1 if item2 should appear before item1,
                    0 if item1 and item2 are the same
     */
    public int compare(Item item1, Item item2) {
        // Sort by field1 descending
        int compareField1 = item1.getField1().compareTo(item2.getField1());
        if (compareField1 != 0) {
            return -compareField1;
        }
        // For equal field1, sort by field2 ascending
        int compareField2 = item1.getField2().compareTo(item2.getField2());
        if (compareField2 != 0) {
            return compareField2;
        }
        // For equal field1 and field1, sort by item id
        return item1.getId().compareTo(item2.getId());
    }
}


technorati tags:, ,

Share/Save

Posted in Java, General | 3 Comments »

Migrate Lucene annotations 3.2.1GA to Hibernate Search 3.0.0.Beta1: object removal

May 9th, 2007 by Ioan Cocan

Changes in Hibernate Search are quite frequent, as it is in continued development. Today I found myself looking into a problem generated by migration from Hibernate-Lucene integration in 3.2.1.GA to the new Hibernate Search 3.0.0.Beta1.

The problem: deletion of an object in Hibernate will not trigger the removal from the Lucene index.

First, check the event listener: using a custom implementation I can see the onPostDelete method is called correctly. Still, no deletion from index and no error message either. Looking in the Hibernate Search sources I can see that the deletion uses the DocumentId marker in order to identify the field used for deletion. The old @Keyword(id=true) is deprecated and seems it no longer works.

The solution: Change the annotation from @Keyword(id=true) to @DocumentId. This will help Hibernate search do its magic.

Share/Save

Posted in Java, General | No Comments »

Error reading tld listeners java.lang.NullPointerException

May 9th, 2007 by Marius Hanganu

I’ve been bugged by this problem for some time now, so I decided investigating a bit. The error shows up when tomcat is trying to reload the web application.


The full stacktrace is shown below:


SEVERE: Error reading tld listeners java.lang.NullPointerException
java.lang.NullPointerException
at org.apache.log4j.Category.isEnabledFor(Category.java:746)
at org.apache.commons.logging.impl.Log4JLogger.isTraceEnabled(Log4JLogger.java:327)
at org.apache.catalina.startup.TldConfig.tldScanResourcePaths(TldConfig.java:582)
at org.apache.catalina.startup.TldConfig.execute(TldConfig.java:283)
at org.apache.catalina.core.StandardContext.processTlds(StandardContext.java:4307)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4144)
at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3025)
at org.apache.catalina.loader.WebappLoader.backgroundProcess(WebappLoader.java:432)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1278)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1570)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1579)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1579)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1559)
at java.lang.Thread.run(Thread.java:595)



The solution is quite simple: replace commons-logging.jar from your 1.1 version to a previous version (1.0.4). That’s it. The problem has been discussed here.

technorati tags:, , , ,

Share/Save

Posted in Java, General | 1 Comment »

« Previous Entries