Book | JIRA | Confluence | Forum

20041204 Saturday December 04, 2004

Import tag for XML bean definitions Category: Spring Live

Juergen has developed out the code for adding an "import" tag to Spring's XML files - now he's looking for feedback. Do you think this is a good feature? Will you use it? I know that Ant and WebWork both have this tag/feature. Personally, I can't see its usefulness but I doubt I'll use it - at least not right away. Most of the apps I've been writing lately have been keen on simplicity and maintainability. The explicit reference to the files used in your web.xml's "contextConfigLocation" seems a bit easier and more explicit. Then again, using something like "applicationContext-*.xml" is even easier (though not as explicit). (2004-12-04 18:29:49.0) Permalink

Installing the Spring IDE for Eclipse Category: Spring Live

I decided to try installing the latest release of the Spring IDE for Eclipse - which is really just a plugin (right?). I figured I'd document my experience here to help others out. Hopefully it's quick and easy. These instructions were writting using Eclipse 3.0 on Windows XP with JDK 1.4.2.

  • Open Eclipse. Go to Help -> Software Updates -> Find and Install. Select "Search for new features to install". Click Next.
  • Click "New Remote Site". Enter "Spring Plugin" for the Name and "http://springframework.sourceforge.net/spring-ide/eclipse/updatesite/" for the URL. Click OK and wait.
  • You should now see a + mark next to "Spring Plugin". Click on that and check both "Spring Framework" and "Spring IDE", then click Next.
  • I tried selecting the bottom two: Spring Framework 1.0.2 and Spring IDE - Beans Configuration Support. The 2nd one requires plug-in "org.eclipse.gef". It looks like I have to download and install that before continuing. It'd be nice if there was an update site for GEF.
  • After downloading the zip, open it and extract it to the folder that holds your "eclipse" folder. For instance, I keep Eclipse in C:\Tools - so I extracted the archive there. You'll have to close and restart Eclipse for this plug-in to be recognized (AFAIK).
  • Help -> Software Updates -> Find and Install. Select "Search for new features..." and click Next. Selecting the bottom two to install should work now - proceed with the installation. Restart Eclipse (as prompted) when finished.
  • To use this nifty plug-in in your project, follow the fresh installation instructions. One thing I noticed is that there's not real-time validation of property settings - it only notifies you after you've saved the file.
  • Cool Features:
    • When you're in a bean's file - right click and select "Show in Spring Beans View". If that doesn't open a beans navigation pane on the right of your screen - go to Window -> Show View -> Other, expand Spring IDE and select Spring Beans.
    • To view a file's "beans graph", right-click on the file in the Beans View and select Show Graph. This will give you pretty pictures like this one. These graphs are even clickable, which is very cool!

This looks to be a great plugin - thanks Torsten! (2004-12-04 15:41:38.0) Permalink Comments [5]

HSQLDB - no JDBC 3.0 drivers Category: Spring Live

Tonight I implemented the UserDAO with Spring JBDC. In most cases when you're inserting a new database record, you have to (1) insert the primary key yourself (2) or use the getGeneratedKeys() method of the JDBC 3.0-compliant driver. Through some simple testing, Spring reports that the HSQLDB Driver (v1.7.2) is not 3.0 compliant. So I have two choices: (1) insert the PKs myself - which doesn't seem very real-world, or (2) change to a database that does have a 3.0 Driver - like MySQL. Whaddya think - would you mind if I changed to using MySQL in Chapter 7?

BTW, the ability to get auto-generated keys from a JDBC 3.0 Driver was added in 1.1. I wonder if there was another way to get auto-generated keys before 1.1? Maybe "select max(id) from table". ;-) (2004-12-04 15:40:53.0) Permalink Comments [14]

Loading Velocity templates from the database using Spring Category: Spring Live

After figuring out how to use Spring and Velocity to send e-mail, I was left with two issues:

  • One thing that seems to wrong with this is that when I run my PositionManagerTest JUnit test - it initializes Velocity a number of times. This is because the PersonManagerImpl is re-initialized each time in my setUp() method. This is a JUnit issue, not a Spring issue. I could probably do something so the PositionManagerImpl is only created once for the entire Test run. Either that, or figure out a way to initialize Velocity for only one test.
  • Another issue is that I'd like to use Velocity's DataSourceResourceLoader, but it only accepts a JNDI DataSource name. It'd be nice if there was an alternative version that would allow setting of the DataSource via IoC.

I'm pleased to report that both of these issues have been solved. The first one was solved by using a static block/variable for loading the ApplicationContext.

The 2nd issue was not as easily solved. However, with a little help from Will Glass-Husain and Juergen - it's now easy to do. You just need to wire the DataSourceResourceLoader with a dataSource and then pass it to the VelocityEngineFactoryBean. Below is the XML to make this happen. If someone needs this right away, I can send you the JARs (Spring and Velocity) that I'm using in my app.

<!-- Configure Velocity for sending e-mail -->
<bean id="velocityEngine" singleton="true"
    class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="velocityPropertiesMap">
        <map>
            <entry key="resource.loader"><value>ds</value></entry>
            <entry key="ds.resource.loader.instance">
                <ref bean="templateLoader"/>
            </entry>
            <entry key="ds.resource.loader.resource.table">
                <value>template</value>
            </entry>
            <entry key="ds.resource.loader.resource.keycolumn">
                <value>name</value>
            </entry>
            <entry key="ds.resource.loader.resource.templatecolumn">
                <value>content</value>
            </entry>
            <entry key="ds.resource.loader.resource.timestampcolumn">
                <value>last_modified</value>
            </entry>
        </map>
    </property>
</bean>
<!-- Velocity Database Template Loader -->
<bean id="templateLoader" singleton="true"
    class="org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader">
    <property name="dataSource"><ref local="dataSource"/></property>
</bean>

You might ask why I want to load my templates from the database instead of the file system? The main reason is because I've built a UI for editing templates through the web. This allows the editing of the templates to become an adminstrator's job, rather than a developer's job -> less maintenance work for me. ;-) (2004-12-04 09:41:19.0) Permalink Comments [5]

Sending Velocity-based E-Mail with Spring Category: Spring Live

One of the requirements on my current project is to send Job Applicants an e-mail when they apply for a position. Since we're using Spring, I figured I'd try out its JavaMail and Velocity support to send this e-mail. Below is a short tutorial for setting up Spring's JavaMail support on a PositionManager class, followed by replacing the e-mail's text with a Velocity template. It's possible there's easier ways to do this, but this is what worked for me.

Step 1: Configure the JavaMailSenderImpl

The first step is to setup a MailSender for the PositionManager. To do this, you need to configure a JavaMailSenderImpl with a host or a session. For a host, it's rather simple. Add the following to your applicationContext.xml file:

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host"><value>localhost</value></property>
    <!-- If you don't want to hardcode "localhost", load it from a mail.properties file 
         with PropertyPlaceholderConfigurer -->
</bean>

Optionally, you can also configure it with a Session from JNDI when you're running in a servlet container:

<bean id="mailSession" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName"><value>java:comp/env/mail/Session</value></property>
</bean>
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="session"><ref bean="mailSession"/></property>
</bean>

I use the first option for JUnit tests, and the 2nd when running in Tomcat. Thanks to Juergen for showing me how easy the JNDI setup is. ;-)

Step 2: Configure a SimpleMailMessage with default values

Next you can configure a SimpleMailMessage with some default values in your applicationContext.xml file:

<bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage">
    <property name="from">
        <value>
            <![CDATA[Human Resources <[email protected]>]]>
        </value>
    </property>
    <property name="subject"><value>Your application has been received</value></property>
</bean>

Step 3: Configure dependencies in PositionManagerImpl

Then in traditional Spring-style, you need to add variables and setters to the PositionManagerImpl class:

    private MailSender mailSender;
    private SimpleMailMessage message;
    public void setMailSender(MailSender mailSender) {
        this.mailSender = mailSender;
    }
    public void setMessage(SimpleMailMessage message) {
        this.message = message;
    }

Then configure this class's definition in applicationContext.xml so Spring will inject its dependencies:

<bean id="positionManagerTarget" class="org.appfuse.service.PositionManagerImpl">
    ...
    <property name="mailSender"><ref bean="mailSender"/></property>
    <property name="message"><ref bean="mailMessage"/></property>
    ...
</bean>

Now you should be able to easily send an e-mail in a method of this class:

    // user and position objects looked up...
    SimpleMailMessage msg = new SimpleMailMessage(this.message);
    msg.setTo(user.getFullName() + "<" + user.getEmail() + ">");
    StringBuffer txt = new StringBuffer();
    txt.append("Dear " + user.getFullName() + ",\n\n");
    txt.append("Thank you for application for our ");
    txt.append(position.getName() + " position. You can check ");
    txt.append(" on the status of this position at the URL below.\n\n");
    // the URL below doesn't really exist ;-)
    txt.append("    http://raibledesigns.com/positions/status.jsp\n\n");      
    txt.append("Sincerely, \n\nRaible Designs Human Resources");
    msg.setText(txt.toString());
    try {
        mailSender.send(msg);
    } catch (MailException ex) {
        log.error(ex.getMessage());
    }

The only problem with this is that the e-mail message is hard-coded into our Java code - so let's refactor it to use a Velocity template for the text.

Step 4: Configuring Velocity in applicationContext.xml

The next step is to use Spring's Velocity support classes to configure a VelocityEngine. For this, add the following to your applicationContext.xml file:

<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="velocityProperties">
        <props>
            <prop key="resource.loader">class</prop>
            <prop key="class.resource.loader.class">
                org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
            </prop>
        </props>
    </property>
</bean>

NOTE: You can also use <property name="configLocation">velocity.properties</property> if velocity.properties file is in your classpath. However, my velocity.properties file has a webapp.loader defined in it, and since this depends on javax.servlet.ServletContext, I didn't want to use it in my business logic layer. You could also load velocity.properties with PropertyPlaceholderConfigurer and then refer to ${class.resource.loader.class}.

Step 5: Configure Velocity dependency in PositionManagerImpl

In order to use this nice little velocityEngine you just configured, you'll need to add a variable and setter to PositionManagerImpl:

    private VelocityEngine velocityEngine;
    public void setVelocityEngine(VelocityEngine velocityEngine) {
        this.velocityEngine = velocityEngine;
    }

And configure it's dependency in applicationContext.xml:

<bean id="positionManagerTarget" class="org.appfuse.service.PositionManagerImpl">
    ...
    <property name="velocityEngine"><ref bean="velocityEngine"/></property>
    ...
</bean>

Now you can refactor the text part of the previous e-mail sending logic to use a template.

    Map model = new HashMap();
    model.put("user", user);
    model.put("position", position);
    String result = null;
    try {
        // notificationTemplate.vm must be in your classpath
        result = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine,
                        "notificationTemplate.vm", model);
    } catch (VelocityException e) {
        e.printStackTrace();
    }
    msg.setText(result);

Pretty slick huh? A further configuration option is to use Spring to set the name of the template. If you know of any better ways to do JavaMail and e-mail templates with Spring, or find errors in my code - please let me know.

One thing that seems to wrong with this is that when I run my PositionManagerTest JUnit test - it initializes Velocity a number of times. This is because the PersonManagerImpl is re-initialized each time in my setUp() method. This is a JUnit issue, not a Spring issue. I could probably do something so the PositionManagerImpl is only created once for the entire Test run. Either that, or figure out a way to initialize Velocity for only one test. Hints would be great.

Another issue is that I'd like to use Velocity's DataSourceResourceLoader, but it only accepts a JNDI DataSource name. It'd be nice if there was an alternative version that would allow setting of the DataSource via IoC.

Update: One reader (name withheld until I get permission) sent me an alternative way of doing this. This method basically removes the need for a MailSender object and instead uses a Session created from your System properties (defaults to localhost). The big improvement on this method is you put the e-mail header into the template. For instance:

From: $from
To: $user.email
Subject: $subject
$content, text, etc.

Here is the modified code to make this work. Notice that the "from" and "subject" variables are still grabbed from Spring's config file:

    Map model = new HashMap();
    model.put("from", msg.getFrom());
    model.put("subject", msg.getSubject());
    model.put("user", user);
    model.put("position", position);
    String result = null;
    try {
        result = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine,
                         this.emailTemplate, model);
    } catch (VelocityException e) {
        e.printStackTrace();
    }
    // set up mail session
    Properties props = System.getProperties();
    Session session = Session.getDefaultInstance(props, null);
    SMTPMessage message = new SMTPMessage(session,
           new ByteArrayInputStream(result.getBytes()));
    SMTPTransport.send(message);

The major reason for using this method over the previous is that the template is a bit friendlier and business-types can easily modify it, add cc's, etc. The major problem with it is that it defaults to localhost unless you set a system property. If there was a way to wire up javax.mail.Session with Spring, you could easily change this. However, since there's no constructor or setProperties() method, it's not staring me in the face. Maybe you can set system properties with Spring as a workaround?

Update 2: This has been published as an article on TheServerSide and Juergen has some additional tips about Spring's mail support.

Update 3: I solved the two issues I had: initializing Velocity more than once, and loading templates from the database. (2004-12-04 09:40:49.0) Permalink Comments [11]

Chapter 10 to be published Monday Category: Spring Live

Chapter 10 (Transactions) has completed the editing process and has been PDFed. Unfortunately, SourceBeat's publish-for-download guy is out of town and won't be able to upload it until Monday. Sounds like I need to learn more about the publishing process so I can publish it myself. ;-)

Hopefully it'll be worth the wait. (2004-12-04 04:42:13.0) Permalink Comments [0]

Use inner beans to simplify XML Category: Spring Live

Dion has a great tip on using inner beans to simplify your Spring bean definitions. Thanks Dion - I'm going to start using this right away. I'll be switching to this for Chapter 2 and AppFuse - it just seems cleaner. (2004-12-04 04:37:58.0) Permalink

Jetspeed 2 now uses Spring Category: Spring Live

From the spring-user mailing list:

Jetspeed 2 has offically moved to using Spring Framework (core, aop, context) for all of it's IoC and AOP needs ;) It only took me 3 days to migrate from Pico/Nano container to Spring and that is without any prior knowledge of Spring! Kudos to the Spring crew for such a great project! [Scott T. Weaver]

Anyone using Jetspeed 2? Would you recommend it for a new portal project? (2004-12-04 04:37:12.0) Permalink

SourceBeat joins ObjectWeb, announces JOnAS Book Category: Author

Yeah baby. Both Geronimo and JOnAS titles should be available by the end of the year.

SourceBeat Publishing today announced its membership in ObjectWeb, an international consortium promoting the development of open-source infrastructure software and middleware.
...
"We are extremely pleased to be a part of the ObjectWeb consortium. With our publishing of JOnAS and other ObjectWeb projects in the future, SourceBeat hopes to further increase the traction and acceptance of the excellent work within ObjectWeb," said Matt Filios, CEO of SourceBeat.

"The ObjectWeb consortium is open to all companies that choose to support the development of enterprise-grade open-source infrastructure software in a collaborative and sustainable way. In that respect, we are delighted to welcome SourceBeat, whose contribution will be pivotal to complement our technical offering with high-quality, updated reference books as requested by corporate users and developers," said Francois Letellier, ObjectWeb's Director of Communications. [Read More]

Kinda funny to see my name mentioned in the "About SourceBeat" section. I'm in the midst of putting the final touches on Chapter 6, which looks like it'll be around 30 pages.

I have the week off for OSCON, so today is a "work on the book" day. Tomorrow morning, I fly out to Portland. My parent's live an hour south (in Salem), so it should be a fun week. Tomorrow night: SourceBeat BBQ at The Raibles! (2004-12-04 04:36:41.0) Permalink

Simplifying [spring:bind] in JSPs Category: Spring Live

Seth has figured out a nice way to help with creating JSP pages that work with many <spring:bind> calls. It minimizes the amount of typing when working with the paths for <spring:bind>. Seth's method basically involves using a few JSP 2.0 tagfiles. While this seems to work nicely, Spring's JSP syntax still seems awful ugly to me. Let's compare this Spring version that requires less typing to the Struts version:

<spring:setNestedPath path="commandName">
  <spring:bind path="firstName">
  <input type="text" name="${status.expression}" value="${status.value}"/>
  </spring:bind>
  <spring:bind path="lastName">
  <input type="text" name="${status.expression}" value="${status.value}"/>
  </spring:bind>
</spring:setNestedPath>

Struts version:

<html:text property="firstName"/>
<html:text property="lastname"/>

If Spring ever expects to compete with Struts for easy-to-create JSPs, it needs to have a rich set of tag libraries like Struts. WebWork has done this right, by creating a rich set of tag libraries that actually go above and beyond the call of duty. Not only do they allow you to write an entire row in a table with a tag, they allow you to customize the HTML in a Velocity template. I'd love to see this feature extracted from WebWork and used in all JSP-based MVC Frameworks. It simply seems like the best way. (2004-12-04 04:36:18.0) Permalink Comments [5]


 
Weblog Archives
Click this photograph for National Geographic's Photo of the Day
Photo of the Day
XML
Spring Logo
+ SourceBeat Weblogs

+ Spring Framework

Spring Live Book
• Raible Designs
• Who is Matt Raible?
AppFuse
Recent Comments