Monday, September 27, 2010

GWT: Developer Mode and Spring Contex Listener

I could not get the usual Spring web context listener to work with GWT 2.0.4 in developer mode, running within Eclipse.

Usually the context listener will load the bootstrap Spring files and then parse the classpath to do auowiring, etc. Running the GWT application in Developer mode failed to parse the classpath so any @Autowired annotations threw exceptions with no bean found error message.

The same Spring files loaded fine when loaded "old school way" - IE remove the web context loader listener and use a manual creation of the Spring application context using the same XML files.

Code Change:

In order to get the Developer mode working within Eclipse, I followed what other monkeys had done and nastily copy & paste the GWT class used to start up Developer mode (which is essentially a Jetty-specific class) and made one small change to get it working for Spring classpath parsing.

The class is called com.google.gwt.dev.shell.jetty.JettyLoader
located here: gwt-src-2010-09-02/trunk/dev/core/src (where gwt-src-2010-09-02 is the root of where the GWT code was checked out to from Subversion).

I copy the whole class as a new class called SpringJettyLoader and changed it as follows:
private final ClassLoader bootStrapOnlyClassLoader = new ClassLoader(null) {};
to
private final ClassLoader bootStrapOnlyClassLoader = Thread.currentThread().getContextClassLoader();

I also had to copy the additional class called JettyNullLogger and include it with my new loader class to reduce hassles.

Running GWT Web Application:

A change is required to the Eclipse Run Configuration used to run your GWT web application. Open up the Run Configuration.
On the Server (2nd) tab, uncheck the "Run built-in server" option.
On the Arguments tab, change the current arguments to include your new class as the -server option.
-noserver 
-remoteUI "${gwt_remote_ui_server_port}:${unique_id}" 
-startupUrl PropertyWeb.html 
-server com.tradecraft.property.web.server.jetty.SpringJettyLauncher 
-logLevel INFO 
-war /home/aisling/development/workspaces/workspace2/property-web/war com.tradecraft.property.web.PropertyWeb

Eclipse Classpath:

I was using m2eclipse to do my dependency resolution for me within Eclipse. This ended up being more trouble than it was worth as I kept getting other dependencies includes that I didn't expect. This especially caused issues with Hibernate 3.3.5.FINAL and JPA 1.0 and 2.0.

I ended up putting all my libs in the usual WEB-INF/lib directory and all the other Eclipse projects specifically pointed to those jars.

Eclipse Source Folders Additional Note:

Removing m2eclipse from the projects, left the classes output pointing to /target/classes which is fine but then I attempted to change all the output directories to /bin. The previously added source folders contain to point to the old location and you wonder why nothing works.

It is cleaner to remove all the source folders within each Eclipse project and then re-add them as they then point to where you expect them to.

Thursday, September 16, 2010

GWT: Custom context menu on a Tree

Monkeys, in order to add a custom pop up menu to a tree (or any similar widget I suppose) which is displayed instead of the usual browser's context menu when you right-click on the tree, you can do the following:

Create a Tree class that implements the correct interface for adding a context menu handler:
package com.tradecraft.property.web.client.view.type;

import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
import com.google.gwt.event.dom.client.HasContextMenuHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Tree;

public class MenuContextTree extends Tree implements HasContextMenuHandlers {

  @Override
  public HandlerRegistration addContextMenuHandler(ContextMenuHandler handler) {
    return addDomHandler(handler, ContextMenuEvent.getType());
  }

}

Then add a handler instance to your tree:
public class PropertyTypesAdminScreen extends BaseClientContentScreen {
  ...
  @Override
  public void initScreenUi() {
    tree = new MenuContextTree();
    tree.addContextMenuHandler(new ContextMenuHandler() {      
      @Override
      public void onContextMenu(ContextMenuEvent event) {
        showContextMenu();
        //Don't let the browser display its default context menu
        event.preventDefault();
      }
    });
    mainPanel.add(tree);
  }

  private void showContextMenu() {
    TreeItem selectedTreeItem = tree.getSelectedItem();
    if (selectedTreeItem != null && selectedTreeItem.getUserObject() != null) {
      //do your thing like display a pop up menu
      MenuDialog menuDialog = new MenuDialog(menuBar);
      menuDialog.showRelativeTo(selectedTreeItem);
    }
  }
}


Wednesday, September 15, 2010

Hibernate SQLQuery

In the last week I have seen this error twice when running native SQL queries to a MySQL database using Hibernate. (I am forced to use SQL - not my first choice.)
No Dialect mapping for JDBC type: -1

It took me a while to find the answer, so I thought I would share.

The problem this time is that the data I was retrieving was a longtext column (not a regular varchar) and this is not automatically mapped by the MySQL dialect (I believe this is fixed in Hibernate 3.5).

I solved the problem by using "addScalar" to define the mapping to use. It worked!


Query query = getSession()
.createSQLQuery("select data from form_data where form_data_id = ?")
.addScalar("data", Hibernate.TEXT)
.setInteger(0, formData.getId());
String oldData = ((String)query.uniqueResult());

Hibernate: Parent and Children --> Retrieving Children using a Filter

Same classes as the previous post but this time to avoid an issue with lazy loading the children outside of a closed Hibernate session, load the parent and its children together in the DAO.

This is slightly more complicated as the data model class now has a status attribute (true || false). Either load all the data or only load the data that is active (If a parent is not active, it is not loaded so none of its children is loaded either).

Using the Criteria API, there doesn't seem to be a way to specify a join criteria on the children join.
You can do it easily using a query:
from Cat as cat
left join cat.kittens as kitten
with kitten.bodyWeight > 10.0
Hence the use of a filter when using the Criteria API. The filter either restricts the children to be active or just loads all of them.

@Repository
public class HibernatePropertyTypeDaoImpl extends BaseHibernateDao implements PropertyTypeDao {
  
  private static final String ACTIVE_WHERE_CLAUSE = "where active = true";
  
  private static Logger LOG = LoggerFactory.getLogger(HibernatePropertyTypeDaoImpl.class);

  @Override
  @SuppressWarnings("unchecked")
  public List getCommercialPropertyTypes(boolean onlyActive) {
    List commercialTypes = (List) getPropertyTypesWithoutChildren(CommercialPropertyType.class, onlyActive);
    for(CommercialPropertyType commercialType : commercialTypes) {
      getChildren(commercialType, onlyActive);
    }
    return commercialTypes;
  }
  
  @SuppressWarnings("unchecked")
  private List getPropertyTypesWithoutChildren(Class clazz, boolean onlyActive) {
    Criteria criteria = sessionFactory.getCurrentSession().createCriteria(clazz); 
    criteria.add(Restrictions.isNull("parent"));
    if (onlyActive) {
      criteria.add(Restrictions.eq("active", true));
    }
    criteria.addOrder(Order.asc("id"));
    return criteria.list();
  }

  @SuppressWarnings("unchecked")
  protected void getChildren(CommercialPropertyType type, boolean onlyActive) {
    List children = null;
    if (onlyActive) {
      children = sessionFactory.getCurrentSession()
                               .createFilter(type.getChildren(), ACTIVE_WHERE_CLAUSE)
                               .list();
    } else {
      children = sessionFactory.getCurrentSession()
                               .createFilter(type.getChildren(), "")
                               .list();
    }
    type.setChildren(children);
    LOG.debug("Set children:"+children);
  } 
}

Tuesday, September 14, 2010

Hibernate: Parent and Children

Mapping Model Class:

A single class is used to represent both the parent and children -> CommercialPropertyType.
(The super class PropertyType just provides additional attributes and is not important.)

@Entity
@Table(name = "commercial_property_type")
public class CommercialPropertyType extends PropertyType {

private CommercialPropertyType parent;
private List children;

...

@ManyToOne
@JoinColumn(name = "parent_id")
public CommercialPropertyType getParent() {
return parent;
}

public void setParent(CommercialPropertyType parent) {
this.parent = parent;
}

@OneToMany(cascade = { CascadeType.ALL })
@JoinColumns({ @JoinColumn(name = "parent_id") })
public List getChildren() {
return children;
}

public void setChildren(List children) {
this.children = children;
}
}

Retrieving Data:

Example DAO method to retrieve the top-level parent data with their children data.

@Repository
public class HibernatePropertyTypeDaoImpl extends BaseHibernateDao implements PropertyTypeDao {

@Override
@SuppressWarnings("unchecked")
public List getCommercialPropertyTypes() {
return (List) getPropertyTypesWithChildren(CommercialPropertyType.class);
}

@SuppressWarnings("unchecked")
private List getPropertyTypesWithChildren(Class clazz) {
Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(clazz);
//Only get parents
criteria.add(Restrictions.isNull("parent"));
//Get the parent's children if there are any
criteria.createAlias("children", "children", CriteriaSpecification.LEFT_JOIN);
criteria.addOrder(Order.asc("id"));
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return criteria.list();
}
}