Tuesday, January 17, 2012

Referencing third-party library source code in a GWT project.


It turns out the process is slightly involved - especially when Maven is added to the mix.  I don't know if this is the blessed way of solving this issue, but it works.

The first thing I had to do was modify the third-party library POM so that it builds the source jars. This can easily be done by adding the following plugin:

  <plugin>
    <artifactId>maven-source-plugin</artifactId>
    <groupId>org.apache.maven.plugins</groupId>
    <version>2.1.2</version>
    <executions>
      <execution>
        <id>attach-sources-jar</id>
        <phase>verify</phase>
        <goals>
          <goal>jar-no-fork</goal>
        </goals>
      </execution>
    </executions>
  </plugin>

Then, I had to run a build/install of the third-party library to install the sources in my local repo.

Next, I needed to modify the gwt-maven-plugin in my project's POM to tell it to compile the sources for my third-party library. For this, I needed the groupId and artifactId of the third-party library. I had already added this as a dependency in my project's POM and was simply able get the information from there. I then had to modify gwt-maven-plugin plugin's configuration element (in the execution element) to add the compileSourcesArtifacts element as follows:

  <compileSourcesArtifacts>
    <compileSourcesArtifact>groupId:artifactId</compileSourcesArtifact>
  <compileSourcesArtifacts>

Where the text "groupId:artifactId" are the actual groupId and artifactId of my third-party library - don't forget the colon character!

Next, I had to note the package name in the third-party library where the objects I wanted to use were located. For this example, let's assume that this package is "com.foo.bar.bat".

I added a package to my GWT project called "com.foo.bar" leaving out the final "sub-package" of "bat." In this package, I created a new gwt.xml (e.g. Foo.gwt.xml) file and used the artifactId as the name capitalizing the first letter (I don't know if this is necessary but it seems convention in GWT). I then populated this file with the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
  "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN"
  "http://google-web-toolkit.googlecode.com/svn/tags/2.4.0/distro-source/core/src/gwt-module.dtd">
<module>
  <source path="bat"/>
</module>

Note that the value of the "path" attribute is the final "sub-package" I left out above. Additionally, this tells GWT to try and generate JavaScript for ALL the classes in the "bat" package. If there are classes that you want to be ignored, change the <source> element to a block-level element and add a child element called <exclude> that has one attribute called "name" that contains the name of the Java file to ignore, e.g., "Bar.java".

Finally, I added a module inherit dependency to my project's gwt.xml file: <inherits name="com.foo.bar.Foo"/>.  Note that I used the capitalized name of the artifactId as the module.

Tuesday, December 13, 2011

Using Spring and MyBatis for non-POJO Data Retrieval

This post will be part one of two dealing with data retrieval in Spring with MyBatis. This post in particular will discuss data retrieval whereby you have a situation where you do not have a DB Schema-to-POJO relationship, e.g. in a properties or settings table.

I won't get into how to set up a Spring datasource bean and will assume you already have one in your Spring config file.  In your Spring config file, per http://code.google.com/p/mybatis/wiki/Spring, add a new bean to configure your SqlSessionFactory using the datasource you defined above.

Next, the mybatis-spring library allows us to wire up things automagically (without declaring our mappers in XML) with the following:

<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="com.foo.persistence"/>
</bean>

This tells the scanner to recursively scan the com.foo.persistence package and create Spring beans for each mapper it finds. If you're using only one datasource, you don't even need to wire that in as the scanner will take care of this for you.

Next, you'll need to create a mapper class like so:

package com.foo.persistence;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface SettingsMapperInterface {

  @Select("SELECT value FROM settings WHERE name = #{name}")
  String getValue(@Param("name") String name);

  @Insert("INSERT INTO settings VALUES(#{name}, #{value})")
  void setValue(@Param("name") String name,
                @Param("value") String value);
}

This defines two methods, one for getting a value from our settings table and one for inserting a value into our settings table. Note that the package name matches what we defined as the "basePackage" for the scanner bean above. Remember that this is recursive so we could have put this in package "com.foo.persistence.settings" and it would still work. In fact, for larger systems, organizing your mappers by package is probably not a bad idea.

Next, let's define a Spring service that is responsible for interacting with the database. This is our DAO layer or business layer. We don't want to pollute our controller with business logic and vice versa - i.e., we don't want to put any UI or action logic in our business layer. Here's what the service class looks like:

package com.foo.service;

import com.foo.persistence.SettingsMapperInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class SettingService {

  public String getValue(String name) {
    return this.settingsMapper.getValue(name);
  }

  @Transactional
  public void setValue(String name, String value) {
    this.settingsMapper.setValue(name, value);
  }

  @Autowired
  private SettingsMapperInterface settingsMapper;
}

Recall how we said above that the scanner automatically creates Spring beans for each of our mappers. Here, you can see we are autowiring this bean into our service class. One note on IDEs, my IntelliJ instance complains about there not being a bean defined with the SettingsMapperInterface type. This is annoying, but it's not a problem. At the time of this writing, I could not find any MyBatis support for IntelliJ.

Finally, we can autowire our service up like any other bean as follows:

package com.foo.controller;

import com.foo.service.SettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class SettingController {

  @RequestMapping(value = "/foo/getSetting",
                  method = RequestMethod.GET)
  public void getSetting(HttpServletRequest request,
                         HttpServletResponse response) {

    String settingName = request.getParameter("settingName");
    String settingValue = this.settingService.getValue(settingName);

    // do something with "settingValue"
  }

  @Autowired
  private SettingService settingService;
}

Here, we pull the name of the setting we want to retrieve off of the request as a parameter (called "settingName") and pass it to the service method for retrieving a setting's value from the database. We can then take this value and do anything we want with it.

Monday, December 5, 2011

Reading Spring Controller and RequestMapping Definitions

My current, major task at work is to bring two existing web applications into one utilizing a single-signon mechanism and overhaul the UI based on my recommendations previously made.  Part of the background architecture work involved in allowing the creation of a menu structure that would ultimately be utilized to create a similar structure in the UI.

I wanted to use JAXB to unmarshall my menu XML into Java objects but I suspect the complex nature of the XML schema and some limitations of the JAXB framework combined with my lack of a complete understanding of JAXB hindered this process to such a degree that I had to stop due to the time I was spending on it.  So, instead of getting the created objects "for free" I had to resort to writing my own SAX Parser event handler to build up the menu structure in Java land.  I had tried a DOM-based approach first, but the recursive nature of the elements made me cringe a bit while writing the code and if you ever have the thought of "there's got to be an easier way to do this" while writing code, your best bet is to just flat out stop what you are doing and take some time to thoroughly think things through before spending any more time on the task.

The event handler also built up the menu item URI paths using attributes on the hierarchical menu structure in the menu XML.  This gave the menu item actions (arbitrary) name-spacing.  I say arbitrary because when using Spring MVC with only one DispatcherServlet, the URI paths are essentially meaningless.

One other thing I wanted to ensure was that we would never have a dangling menu item - that is a menu item defined (and in the UI) but not actually tied back to a Spring Controller method.  So, I created a unit test that loads my menu XML, pulls a list of all the RequestMapping annotated methods, and confirms that every menu item ties back to a server-side method.

Here's the code:

  Map<String, Object> controllers =
    this.ctx.getBeansWithAnnotation(Controller.class);
  for(Map.Entry<String, Object> entry : controllers.entrySet()) {

    Class controllerClass = entry.getValue().getClass();
    for(Method method : controllerClass.getDeclaredMethods()) {

      RequestMapping rm =
        method.getAnnotation(RequestMapping.class);
      if(rm == null) {
        continue;
      }
      else {

        String[] vals = rm.value();
        if (vals.length == 1) {
          this.mappingSet.add(vals[0]);
        }
        else {
          String msg =
            "this test assumes only one request mapping per method.  " +
            "check class: " + controllerClass + " and method: " + method;
          Assert.fail(msg);
        }
      }
    }
  }

Two notes:

  1. The reference "this.ctx" refers to a Spring ApplicationContext, in my case here an instance of the XmlWebApplicationContext class that loads my Spring configuration.
  2. The reference "this.mappingSet" is simply a class-local field (HashSet<String>) in which I simply store the RequestMapping values.
Finally, after this is complete I confirm that every menu item action URI is contained in the set of extracted RequestMapping values.