Posts Tagged ‘velocity

13
Feb
13

Create Custom Maven Plugin

This page describes how to create a custom maven plugin.

Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.

Problem Statement

This site contains hundreds pages with small demo java projects embedded in its pages.

While working on this site I came across a few problems.

  1. How to reduce typo errors on the pages source code.
  2. How to keep the blog page up to date after code fixes.

The solution: Have a custom maven plugin generate the blog page with source code automatically. The plugin uses velocity template that contain the text of the blog page along with “#include” velocity directives that bring in the source code from the project.

Implementation

Follow these steps to create a custom plugin to generate HTML content from velocity templates.

Start by editing the pom.xml file to look like this.

vi pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>github.numberformat</groupId>
	<artifactId>blog-plugin</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>maven-plugin</packaging>

	<properties>
		<mavenVersion>2.0.6</mavenVersion>
	</properties>

	<pluginRepositories>
	  <pluginRepository>
	    <id>numberformat-releases</id>
	    <url>https://raw.github.com/numberformat/20130213/master/repo</url>
	  </pluginRepository>
	</pluginRepositories>

	<dependencies>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-plugin-api</artifactId>
			<version>2.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.velocity</groupId>
			<artifactId>velocity</artifactId>
			<version>1.7</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-model</artifactId>
			<version>${mavenVersion}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-artifact</artifactId>
			<version>${mavenVersion}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-project</artifactId>
			<version>${mavenVersion}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-core</artifactId>
			<version>${mavenVersion}</version>
		</dependency>
	</dependencies>
	<name>Wordpress Page Generation Plugin</name>
	<description>Generates a wordpress page for the project.</description>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
	 		<plugin> 			
	 			<groupId>github.numberformat</groupId>
	 			<artifactId>blog-plugin</artifactId>
	 			<version>1.0-SNAPSHOT</version>
	 			<configuration>
				<gitUrl>https://github.com/numberformat/20130213</gitUrl>
	 			</configuration>
	        <executions>
	          <execution>
	            <id>1</id>
	            <phase>site</phase>
	            <goals>
	              <goal>generate</goal>
	            </goals>	            
	          </execution>
	        </executions>
	 		</plugin>
		</plugins>
	</build>
</project>

The following file is the plugin implementation class.

BlogMojo.java

package github.numberformat.plugin;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

/**
 * This is a simple plug-in that generates a blog page from a velocity
 * template. This allows developers to create very simple project documentation
 * pages for their projects. Developers would typically create a blog entry by
 * copying and pasting the HTML directly into the blog site.
 * 
 * @goal generate
 */
public class BlogMojo extends AbstractMojo {
	/**
	 * @parameter default-value="${basedir}"
	 * @required
	 * @readonly
	 */
	private File basedir;

	/**
	 * Represents the date first published.
	 * 
	 * @parameter default-value="https://github.com/numberformat"
	 * @required
	 * @readonly
	 */
	private String gitUrl;
	
	
	public void execute() throws MojoExecutionException {
		
		final File templateDir = basedir;
		final File targetBlog = new File(basedir, "target/blog");

		if(new File(templateDir, "src/blog/wordpress.vm").canRead()) {
			if(!targetBlog.exists()) {
				targetBlog.mkdirs();
			} else if(!targetBlog.isDirectory()) {
				throw new MojoExecutionException("Must be a directory: " + targetBlog.getAbsolutePath());
			}
	
	        VelocityEngine ve = new VelocityEngine();
	        ve.setProperty("file.resource.loader.path", templateDir.getAbsolutePath());
	        ve.init();
        
	        Template t = ve.getTemplate( "src/blog/wordpress.vm" );
	        VelocityContext context = new VelocityContext();
	        context.put("blog_header", getHeader());
	        context.put("blog_footer", getFooter());
	        context.put("blog_git_url", gitUrl);
	        
	        FileWriter writer = null;
			try {
				writer = new FileWriter(new File(targetBlog, "wordpress.html"));
		        t.merge( context, writer );				
			} catch (IOException e) {
				throw new MojoExecutionException(e.getMessage());
			} finally {
				try{writer.close();}catch(Exception e){}				
			}
		}
	}


	private Object getFooter() {
		return "<div style=\"font-size:13px;border:1px solid gray; " +
				"padding:5px;line-height:120%\">Full downloadable source for " +
				"this page is <a href=\""+gitUrl+"\">available here</a>. " +
				"</div>";
	}

	private Object getHeader() {
		return "<div style=\"font-size:13px;border:1px solid gray; " +
				"padding:5px;line-height:120%\">Full downloadable source for " +
				"this page is <a href=\""+gitUrl+"\">available here</a>. " +
				"Corrections and enhancements are welcome, fork, change and push " +
				"back to GitHub.</div>";
	}
}

Publish to Nexus or Website

You may deploy the plugin into a nexus repository a simple website.

Example Usage

For demonstration purposes I have published the plugin to the following URL, you may use it in your project by including it in your pom.xml.

vi pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>testProject</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <pluginRepositories>
    <pluginRepository>
      <id>numberformat-releases</id>
      <url>https://raw.github.com/numberformat/20130213/master/repo</url>
    </pluginRepository>
  </pluginRepositories>
  
  <build>
  	<plugins>
  		<plugin> 			
  			<groupId>github.numberformat</groupId>
  			<artifactId>blog-plugin</artifactId>
  			<version>1.0-SNAPSHOT</version>
	        <executions>
	          <execution>
	            <id>1</id>
	            <phase>site</phase>
	            <goals>
	              <goal>generate</goal>
	            </goals>	            
	          </execution>
	        </executions>
  		</plugin>
  	</plugins>
  </build>
</project>

As you can see from the above the plugin to generate the wordpress page is hooked into the “site” phase of the build lifecycle.

Velocity Template

Save the file into:

src/blog/wordpress.vm

#set( $foo = "Velocity" )
Hello $foo World!

(include sourcecode tag in square brackets around the include line below)
#include("src/main/java/App.java")
(include /sourcecode tag in square brackets around the include line above)

Run the Plugin

To run the plugin and you have specified the executions tag above just type

mvn site

As an alternative if you don’t want to hook it into the maven lifecycle then just delete the “executions” tag above and run the plugin by typing:

To run the plugin just type

mvn blog:generate

If you get a WARNING about plexus ignore it.

Upon successful build you can view the generated wordpress page in the target/blog folder of the project.

Next Steps

blog-plugin improvements:

  1. Enhance the plugin to generate pages in formats other than wordpress.
  2. Have plugin insert headers or footers on the generated pages. (done)
  3. Enhance the plugin to have blog pages contain a link to GitHub where visitors can simply checkout the project instead of copying and pasting source from the page. (done)
Full downloadable source for this page is available here.
10
Dec
09

Automated Email using Spring Framework and Velocity

This page describes the process one can take to send automated emails based on a velocity template, Maven and the Spring Framework. The page also covers how to send email with inline resources. These emails have the images included in the message as mime attachments.

Requirements

  • Velocity Template Library
  • Spring Framework
  • Maven
  • SMTP port 25 access (eg. mail.yourhostname.com)

This page requires you have access to a mail server that can accept mail from your machine. You may typically test this by running telnet mail.yourhostname.com 25. Test this by manually sending a message and verify that it gets sent.

Start the project

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

groupId: com.test
artifactId: test-email

Hit enter for the rest of the questions leaving them default.

After creating the project edit the pom.xml file and add the following dependencies.

        <dependency>
          <groupId>javax.mail</groupId>
          <artifactId>mail</artifactId>
          <version>1.4</version>
        </dependency>
        <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring</artifactId>
                <version>2.5.6</version>
        </dependency>
        <dependency>
                <groupId>velocity</groupId>
                <artifactId>velocity</artifactId>
                <version>1.5</version>
        </dependency>

Creating a template

The first step in the process is to create a email template. The template contains email layout. This is similar to creating a JSP page.

<html>
<body>
<h3>Hi ${user.userName}, welcome!</h3>

<div>
   Your email address is <a href="mailto:${user.emailAddress}">${user.emailAddress}</a>.
</div>
</body>
</html>

Configuration

Since we are using the autowiring functionality of the spring framework we dont really need to explicitly define the beans in xml. Instead all we need are the next few lines.

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

<!-- The following is used for the @Autowired and @Required, @Resource etc... -->
        <context:annotation-config/>
        <context:component-scan base-package="com.test"/>

   <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
      <property name="host" value="mail.hostname.com"/>
   </bean>

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

</beans>

Creating the application

The following is the code to a simple java main application that will call the method that sends out the email.

package com.test;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.mail.internet.MimeMessage;

import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.stereotype.Component;
import org.springframework.ui.velocity.VelocityEngineUtils;

@Component("app")
public class App {
        private JavaMailSender mailSender;
        private VelocityEngine velocityEngine;

        public JavaMailSender getMailSender() {
                return mailSender;
        }

        @Autowired
        @Required
        public void setMailSender(JavaMailSender mailSender) {
                this.mailSender = mailSender;
        }

        public VelocityEngine getVelocityEngine() {
                return velocityEngine;
        }

        @Autowired
        @Required
        public void setVelocityEngine(VelocityEngine velocityEngine) {
                this.velocityEngine = velocityEngine;
        }

        public static void main(String[] args) {
                // initialize spring context
                ApplicationContext context = new ClassPathXmlApplicationContext(
                                new String[] { "applicationContext.xml" });
                App app = (App) context.getBean("app");
                // call the method
                app.testEmail();
        }

        public void testEmail() {
                final User user = new User();
                user.setEmailAddress("neeraj.verma@hostname.com");
                user.setUserName("verma");

                MimeMessagePreparator preparator = new MimeMessagePreparator() {
                        public void prepare(MimeMessage mimeMessage) throws Exception {
                                MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
                                message.setTo(user.getEmailAddress());
                                message.setFrom("neeraj.verma@hostname.com"); // could be
                                // parameterized...
                                Map model = new HashMap();
                                model.put("user", user);
                                String text = VelocityEngineUtils.mergeTemplateIntoString(
                                                velocityEngine, "com/test/email-template.vm", model);
                                message.setText(text, true);
                        }
                };
                this.mailSender.send(preparator);
        }
}

User

package com.test;

public class User {
        private String userName;
        private String emailAddress;

        public String getUserName() {
                return userName;
        }
        public void setUserName(String userName) {
                this.userName = userName;
        }
        public String getEmailAddress() {
                return emailAddress;
        }
        public void setEmailAddress(String emailAddress) {
                this.emailAddress = emailAddress;
        }
}

Running the code

run the code by executing the following command.

mvn exec:java -Dexec.mainClass="com.test.App"

In-line resources using Velocity Templates.

Change template to include images

<html>
<body>
<h3>Hi ${user.userName}, welcome!</h3>

<div>
   Your email address is <a href="mailto:${user.emailAddress}">${user.emailAddress}</a>.
</div>
<br/>
<img src="cid:identifier1234"/>
</body>
</html>

Convert to send multi-part messages

        MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true); // multipart

Add the image(s)

Be careful with the order in which you add the resources. It may not work if it is not ordered “correctly”.

        FileSystemResource res = new FileSystemResource(new File(
                        "/home/verma/Desktop/wallpaper/wright.jpg"));
        message.addInline("identifier1234", res);

This site is a collaborative effort! The complete text and sourcecode for this is available on GitHub. Corrections and enhancements are welcome, please make the change and submit a pull request in the comment area below.



Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 78 other followers

July 2017
S M T W T F S
« Mar    
 1
2345678
9101112131415
16171819202122
23242526272829
3031  

Blog Stats

  • 822,357 hits