Raging Goblin

17 July 2013

Spring Roo 10: Update images

Filed under: Java,Spring Roo — Raging Goblin @ 20:23
Tags: ,

In my previous blog about uploading images I left it to the reader to come up with a solution for updating an image once a LogItem is created. However, I recently noticed that I was not able to update this image as easy as I thought, due to the fact that the HiddenHttpMethodFilter provided by Spring is not capable of handling multipart forms. This will have the effect that when you click on an update button, you will end up creating a new one as the POST request will end up in this function. This is easy to remedy, but as it took me a while, I will put you in the right direction. We will continue where we left off the last time.

Step 9: Update update.jspx to send a MultipartFile

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:update id="fu_raging_goblin_roo_domain_LogItem" modelAttribute="logItem" multipart="true" path="/logitems/update" versionField="Version" z="user-managed">
        <field:input field="message" id="c_raging_goblin_roo_domain_LogItem_message" z="apnKidZldiFISmA4Q9pumki/o6Q="/>
        <field:datetime dateTimePattern="${logItem_creationdate_date_format}" field="creationDate" id="c_raging_goblin_roo_domain_LogItem_creationDate" z="p5GCO6w9q76CIDoEn17xbZPqlT0="/>
		<img height="256px" src="${logItem.id}/image"/>
        <field:input field="image" id="c_raging_goblin_roo_domain_LogItem_image" type="file" z="user-managed" required="false"/>
        <field:input field="contentType" id="c_raging_goblin_roo_domain_LogItem_contentType" z="user-managed" render="false"/>
        <field:input field="path" id="c_raging_goblin_roo_domain_LogItem_path" render="false" z="user-managed"/>
    </form:update>
</div>

Note several things. First, I changed the multipart to true, just like the create.jspx page. This will result in the error I described. Therefore I changed the path item so that it points to another path. Secondly, I set the input for the image to ‘not required’ as you might want to choose not to change the image. For the rest, changes are similar to create.jspx.

Step 10: Add an update method to the controller
The update method should be moved from the LogItemController_Roo_Controller aspect to the controller, which by now should be a very familiar trick. Change it to:

@RequestMapping(value = "/update", method = RequestMethod.POST, produces = "text/html")
	public String update(@Valid LogItem logItem, BindingResult bindingResult,
			Model uiModel, HttpServletRequest httpServletRequest) {
		if (bindingResult.hasErrors()) {
			populateEditForm(uiModel, logItem);
			return "logitems/update";
		}
		uiModel.asMap().clear();

		CommonsMultipartFile image = logItem.getImage();
		if (image != null && image.getSize() != 0) {
			File file = new File(image.getOriginalFilename());
			try {
				image.transferTo(file);
				logItem.setContentType(image.getContentType());
				logItem.setPath(file.getAbsolutePath());
			} catch (Exception e) {
				e.printStackTrace();
				return "logitems/update";
			}
		} else {
			LogItem logItemFromDb = LogItem.findLogItem(logItem.getId());
			logItem.setContentType(logItemFromDb.getContentType());
			logItem.setPath(logItemFromDb.getPath());
		}

		logItem.merge();
		return "redirect:/logitems/"
				+ encodeUrlPathSegment(logItem.getId().toString(),
						httpServletRequest);
	}

As you see, I put the method on the path /update, which corresponds with the value in update.jspx. The rest is pretty standard in my humble opinion.

Advertisements

16 May 2013

Spring Roo 9: Uploading images 2 (store images on filesystem)

Filed under: Java,Spring Roo — Raging Goblin @ 20:39
Tags: ,

From the previous blog it is pretty easy to guess how to do this, but here is the recipe as promised to upload images and store them on the file system. As the images are not stored in the database you can use MySQL for this as well.

Step 1: Add maven dependencies
Same as previous.

Step 2: Add properties to the ‘LogItem’

private String contentType;
private String path;
@Transient
private CommonsMultipartFile image;

Note here that the image is added as a CommonsMultipartFile, exactly as it is received by Spring, but it is marked @Transient which will prevent it from entering the database. Besides this a path is added to enable us to retrieve the image later.

Step 3: Update create.jspx to send a MultipartFile
Like in step 3 of the previous post, the form to create a LogItem is adjusted to our needs:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:spring="http://www.springframework.org/tags" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:create id="fc_raging_goblin_roo_domain_LogItem" modelAttribute="logItem" multipart="true" path="/logitems" render="${empty dependencies}" z="user-managed">
        <field:input field="message" id="c_raging_goblin_roo_domain_LogItem_message" z="apnKidZldiFISmA4Q9pumki/o6Q="/>
        <field:datetime dateTimePattern="${logItem_creationdate_date_format}" field="creationDate" id="c_raging_goblin_roo_domain_LogItem_creationDate" z="p5GCO6w9q76CIDoEn17xbZPqlT0="/>
        <field:input field="image" id="c_raging_goblin_roo_domain_LogItem_image" type="file" z="user-managed"/>
        <field:input field="contentType" id="c_raging_goblin_roo_domain_LogItem_contentType" render="false" z="user-managed"/>
        <field:input field="path" id="c_raging_goblin_roo_domain_LogItem_path" z="P0zJQZkpJ19wRptpdaKLWSTr/BU=" render="false"/>
    </form:create>
    <form:dependency dependencies="${dependencies}" id="d_raging_goblin_roo_domain_LogItem" render="${not empty dependencies}" z="Qbb30wyy/eoxYyR+b/J66Ec1Lxw="/>
</div>

Edit 17-07-2013: See next post how to handle updates to an image.

Step 4: Update input.tagx to handle type=”file”
If you have not done so yet, adjust it as described in step 4 earlier.

Step 5: Add a method to the controller to send the actual image
Images are now loaded from the filesystem, to be precise from the path field:

@RequestMapping(value = "/{id}/image", method = RequestMethod.GET)
public String showImage(@PathVariable("id") Long id,
		HttpServletResponse response, Model model) {
	LogItem logItem = LogItem.findLogItem(id);
	if (logItem != null && logItem.getPath() != null) {
		try {
			OutputStream out = response.getOutputStream();
			response.setContentType(logItem.getContentType());
			Files.copy(FileSystems.getDefault().getPath(logItem.getPath()),
					out);
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	return null;
}

Step 6: Change create and update methods of LogItemController to save image to the filesystem
The create and update methods now should store the image on the filesystem. With the new file-io of Java 7 this has never been easier.

@RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(@Valid LogItem logItem, BindingResult bindingResult,
		Model uiModel, HttpServletRequest httpServletRequest) {
	if (bindingResult.hasErrors()) {
		populateEditForm(uiModel, logItem);
		return "logitems/create";
	}
	uiModel.asMap().clear();

	CommonsMultipartFile image = logItem.getImage();
	if (image != null) {
		File file = new File(image.getOriginalFilename());
		try {
			image.transferTo(file);
			logItem.setContentType(image.getContentType());
			logItem.setPath(file.getAbsolutePath());
		} catch (Exception e) {
			e.printStackTrace();
			return "logitems/create";
		}
	}
	logItem.persist();
	return "redirect:/logitems/"
			+ encodeUrlPathSegment(logItem.getId().toString(),
					httpServletRequest);
}

Note that the images will end up in the same directory where your webapp is running. This is something you probably wouldn’t do on a production system.

Step 7: Give webserver write access to the filesystem
Take care that the directory where you want to save the images is write-enabled for the webserver. Ok, I admit this is not really a decent step, but I just wanted to keep the numbering the same as in the previous post and this is the best I could come up with ;).

Step 8: Update show.jspx to display the image
The artificial step 7 allows me now to just redirect you to the previous step 8.

5 May 2013

Spring Roo 8: Uploading images 1 (store images as blob in database)

Here is some hard gained knowledge. How to provide the possibility to upload (and view) images to a Spring Roo website. It took me quite a while to understand how to do this and I will describe 2 ways of doing it. In this post I will show a method with storing the image as a byte-array in the database, in the next one I will describe how to store the images on disk. Each method has it’s own benefits so take your pick. The reference is our logbook application introduced in previous posts. For some reason I never have been able to get this working with MySQL, it will generate a tinyblob field which is not capable of storing my images, and using @Lob on the field causes a java.lang.AbstractMethodError: org.apache.commons.dbcp.DelegatingPreparedStatement.setBinaryStream(ILjava/io/InputStream;J). I don’t know how to solve this, so we switch to PostgreSQL. This might be a better choice anyway as I am not a particular fan of dinosaurs like Larry Ellison or Steve Ballmer. Now take care that PostgreSQL is a completely different beast than MySQL. Try to setup your database properly with the proper owner and access rights on the server and the database itself before you continue. I will not tell you how to do that as there is plenty of stuff about this on the internet and I don’t want to deprive you of a fine ‘cursing and banging your head against a wall’ adventure.

Edit 17-05-2013: See comments below, update commons-dbcp to version 1.4 and you will do fine with MySQL as well.

To start with the most simple method, here are the steps needed to store the images as blobs in the database:

Step 1: Add maven dependencies
To handle uploads Spring relies on 2 dependencies: commons-fileupload.jar and commons-io.jar. The first one is already in the pom, so only the latter should be added:

<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.4</version>
</dependency>

If you use the STS, right click on your project and update the maven dependencies.

Step 2: Add properties to the ‘LogItem’

private byte[] image;
private String contentType;

Take care that the Roo shell is running, you will notice that Roo updates the views and the ApplicationConversionFactoryBean as needed.

Step 3: Update create.jspx to send a MultipartFile
The view for creating a LogItem (~/src/main/webapp/WEB-INF/views/logitems/create.jspx) should be manually adjusted to provide an upload field:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:spring="http://www.springframework.org/tags" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:create id="fc_raging_goblin_roo_domain_LogItem" modelAttribute="logItem" multipart="true" path="/logitems" render="${empty dependencies}" z="user-managed">
        <field:input field="message" id="c_raging_goblin_roo_domain_LogItem_message" z="apnKidZldiFISmA4Q9pumki/o6Q="/>
        <field:datetime dateTimePattern="${logItem_creationdate_date_format}" field="creationDate" id="c_raging_goblin_roo_domain_LogItem_creationDate" z="p5GCO6w9q76CIDoEn17xbZPqlT0="/>
        <field:select field="logUser" id="c_raging_goblin_roo_domain_LogItem_logUser" itemValue="id" items="${logusers}" path="/logusers" z="RJfGPnxQnmkMqPwvKANgyfbJA6g="/>
        <field:input field="image" id="c_raging_goblin_roo_domain_LogItem_image" type="file" z="user-managed"/>
        <field:input field="contentType" id="c_raging_goblin_roo_domain_LogItem_contentType" z="user-managed" render="false"/>
    </form:create>
    <form:dependency dependencies="${dependencies}" id="d_raging_goblin_roo_domain_LogItem" render="${not empty dependencies}" z="Qbb30wyy/eoxYyR+b/J66Ec1Lxw="/>
</div>

Note several changes here: multipart=”true” on the form tag, type=”file” on the field tag for the image and render=”false” on the contentType tag. Spring Roo will adjust the z values to user-managed for us, but you can do this yourself as well. Change the update.jspx in the same way. I also removed the image and contentType from list.jspx by setting render=”false” on those columns.
Edit 17-05-2013: See later post on how to update an image.

Step 4: Update input.tagx to handle type=”file”
In the previous step we added type=”file” to the input fields of the images, but this field actually does nothing with this parameter, so add

<c:when test="${type eq 'file'}">
   <form:input type="file" id="_${sec_field}_id" path="${sec_field}" disabled="${disabled}" />
</c:when>

to this tag (~/src/main/webapp/WEB-INF/tags/form/fields/input.tagx) just above the line ‘<c:when test=”${type eq ‘password’}”>’.

Step 5: Add a method to the controller to send the actual image
In order to send the actual image to the browser we need to add an extra REST mapping to the LogItemController. Add the following method:

@RequestMapping(value = "/{id}/image", method = RequestMethod.GET)
	public String showImage(@PathVariable("id") Long id, HttpServletResponse response, Model model) {
		LogItem logItem = LogItem.findLogItem(id);
		if (logItem != null) {
			byte[] image = logItem.getImage();
			if (image != null) {
				try {
					response.setContentType(logItem.getContentType());
					OutputStream out = response.getOutputStream();
					IOUtils.copy(new ByteArrayInputStream(image), out);
					out.flush();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

This will take care of sending the image when you browse to an url like http://localhost:8080/logbook/logitems/1/image. Note that I just added /image to the standard url mapping of a LogItem.

Step 6: Change create and update methods of LogItemController to set contentType
Copy the create method from the aspect to the java file and wait to let Roo update the aspect. Than add a parameter and store the contentType:

    @RequestMapping(method = RequestMethod.POST, produces = "text/html")
    public String create(@Valid LogItem logItem, BindingResult bindingResult, Model uiModel, 
    		@RequestParam("image") MultipartFile multipartFile,
    		HttpServletRequest httpServletRequest) {
        if (bindingResult.hasErrors()) {
            populateEditForm(uiModel, logItem);
            return "logitems/create";
        }
        uiModel.asMap().clear();
        logItem.setContentType(multipartFile.getContentType());
        logItem.persist();
        return "redirect:/logitems/" + encodeUrlPathSegment(logItem.getId().toString(), httpServletRequest);
    }

You will have to edit the update method in a similar fashion.

Step 7: Add a PropertyEditor to the LogItemController
At this moment Spring will send a CommonMultipartFile to the controller, but the type of the image is a byte-array. Spring will fail with a IllegalArgumentException (Failed to convert property value of type org.springframework.web.multipart.commons.CommonsMultipartFile to required type byte[] for property image). Spring however comes very conveniently with a PropertyEditor to handle this conversion. Register this in the LogItemController:

	@InitBinder
	protected void initBinder(HttpServletRequest request,
			ServletRequestDataBinder binder) throws ServletException {
		binder.registerCustomEditor(byte[].class,
				new ByteArrayMultipartFileEditor());
	}

Step 8: Update show.jspx to display the image
To display an image add a standard HTML img tag to show.jspx:

<img src="${logitem.id}/image" height="256px"/>

Screenshot

Good luck!

References

  1. http://viralpatel.net/blogs/spring-roo-save-read-blob-object-spring-roo-tutorial/
  2. http://gadgets.coolman.ca/multipart-file-upload-spring-roo/
  3. http://whyjava.wordpress.com/tag/spring-roo/

2 May 2013

Spring Roo 7: JSTL

Filed under: Java,Spring Roo — Raging Goblin @ 08:02
Tags: , ,

Yesterday I tried to edit a Spring Roo generated view and used one of those handy constructs of the jstl (JavaServer Pages Standard Tag Library), the foreach loop:

<c:forEach items="${vmpadresses}" var="address">
   <input type="text" value="${address}" />
</c:forEach>

When I ran the page it threw a org.apache.jasper.JasperException to me with the message: The prefix “c” for element “c:forEach” is not bound. I thought what, no jstl?! But of course I was wrong, it is just not registered in the generated views. Look for the tag with all the other tag directives. In my case this was the div tag. Ad

xmlns:c="http://java.sun.com/jsp/jstl/core"

to this tag so that it reads something like this:

<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:page="urn:jsptagdir:/WEB-INF/tags/form" version="2.0">

The dependency for the jstl is already in the pom, so this is all you have to do and all is fine again.

18 April 2013

Spring Roo 6: Representation of enums and internationalization

Filed under: Java,Spring Roo — Raging Goblin @ 20:34
Tags: , ,

Today a short post about the representation of enums in Spring Roo and internationalization. First, we are going to add an enum (Gender) to our application:

enum type --class ~.domain.Gender
enum constant --name MALE 
enum constant --name FEMALE
focus --class ~.domain.LogUser
field enum --type ~.domain.Gender --fieldName gender

This will also add the Gender to the user management. The problem with this is the representation, which is ugly and most certainly not usable in a multi language application:
Ugly enum representation.
To change this we install an extra formatter in the ApplicationConversionServiceFactoryBean. Many examples on the web create a dedicated formatter for each enum, but in a real application you will have many enums. It is therefore much easier to install just one formatter for all enums:

package raging.goblin.roo.web;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
import org.springframework.roo.addon.web.mvc.controller.converter.RooConversionService;

@RooConversionService
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean implements ApplicationContextAware {

	private ApplicationContext applicationContext;

	@Override
	protected void installFormatters(FormatterRegistry registry) {
		super.installFormatters(registry);
		registry.addConverter(createEnumConverter());
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	private Converter<Enum<?>, String> createEnumConverter() {
		return new Converter<Enum<?>, String>() {

			@Override
			public String convert(Enum<?> value) {
				String output = value.toString();
				try {
					output = applicationContext.getMessage(value.toString(), null, LocaleContextHolder.getLocale());
				} catch (NoSuchMessageException e) {
					System.err.println("No message resource found for " + value + " add this to the resource bundle");
				}
				return output;
			}
		};
	}
}

Note that the ApplicationConversionServiceFactoryBean implements the ApplicationContextAware interface. Spring will take care of injecting the the ApplicationContext for you.

Add the following lines to ‘~/logbook/src/main/webapp/WEB-INF/i18n/application.properties‘:

#enums
MALE=Male
FEMALE=Female

The result:
Nice enum representation

This also facilitates internationalization of enums. Installing i18n in Spring Roo is quite easy. The command ‘web mvc install language –code nl‘ e.g. adds all necessities to display the user interface in dutch. Of course Roo cannot provide translations for all specific messages and entities, so you will have to add them yourself in ‘~/logbook/src/main/webapp/WEB-INF/i18n/messages_nl.properties‘ and ‘~/logbook/src/main/webapp/WEB-INF/i18n/application_nl.properties‘. In the latter you can provide translations for your enum values, which will make your application fully international:
Dutch enum representation

6 April 2013

Spring Roo 5: Role based views

Filed under: Java,Spring Roo — Raging Goblin @ 20:18
Tags: , ,

The user management functionality introduced in the previous post is not meant to be accessed by all users. This is the reason why we introduced the LogUserRole. Only members of the group ADMINSTRATOR should be able to add, update or delete users. To accomplish this we need 2 things:

  1. Check for the role the logged in user has when executing a method in the LogUserController.
  2. Remove the links to user management from the menus.

The first can be done by using the @PreAuthorize annotation. To enrich our application with this we first add this to webmvc-config.xml. Add the schema definition and enable the annotation by setting the head of this configuration file to:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc" 
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:global-method-security pre-post-annotations="enabled "/>

Note that I added two things, the schema definition and the enabling of the pre-post-annotations.

Now, in the LogUserController we can add the @PreAuthorize annotation. E.g. the head of the method create will look like this:

@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(@Valid LogUser logUser, BindingResult bindingResult, Model uiModel,
	HttpServletRequest httpServletRequest) {

You should do this for all the other methods as well.

We also add a AccessDeniedException configuration to the webmvc-config.xml to let the one who tries to access these methods know what went wrong:

<props>
  <prop key=".DataAccessException">dataAccessFailure</prop>
  <prop key=".NoSuchRequestHandlingMethodException">resourceNotFound</prop>
  <prop key=".TypeMismatchException">resourceNotFound</prop>
  <prop key=".MissingServletRequestParameterException">resourceNotFound</prop>
  <prop key="org.springframework.security.access.AccessDeniedException">accessDenied</prop>
</props>

Add a view definition in src/main/webapp/WEB-INF/views/views.xml:

<definition extends="public" name="accessDenied">
  <put-attribute name="body" value="/WEB-INF/views/accessDeniedException.jspx"/>
</definition>

Create this view with a proper message in src/main/webapp/WEB-INF/views/accessDeniedException.jspx.

The second thing we need to do is change the menus so that a normal user will not access these methods accidentally. This can be done by adding a security tag to menu.jspx and surrounding the user management menu items with a role based security tag:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:jsp="http://java.sun.com/JSP/Page"
	xmlns:menu="urn:jsptagdir:/WEB-INF/tags/menu"
	xmlns:security="http://www.springframework.org/security/tags" id="menu"
	version="2.0">

	<jsp:directive.page contentType="text/html;charset=UTF-8" />
	<jsp:output omit-xml-declaration="yes" />
	<menu:menu id="_menu" z="nZaf43BjUg1iM0v70HJVEsXDopc=">
		<menu:category id="c_logitem" z="SqQ1OSujMWnswClBZ0yGRL10ER0=">
			<menu:item id="i_logitem_new" messageCode="global_menu_new"
				url="/logitems?form" z="/iSFuRAPFJM3+CEo2pV5jdJJVfU=" />
			<menu:item id="i_logitem_list" messageCode="global_menu_list"
				url="/logitems?page=1&amp;size=${empty param.size ? 10 : param.size}"
				z="+VS0ebBCcdnV89xtkN9/QZZFFOw=" />
		</menu:category>
		<security:authorize ifAllGranted="ADMINISTRATOR">
		<menu:category id="c_loguser" z="Ya37W2Dk3lcEcn0gGjq3sCqfdxc=">
			<menu:item id="i_loguser_new" messageCode="global_menu_new"
				url="/logusers?form" z="YxH3QphXpDJh34UhJibtVutDlQw=" />
			<menu:item id="i_loguser_list" messageCode="global_menu_list"
				url="/logusers?page=1&amp;size=${empty param.size ? 10 : param.size}"
				z="De/VBRYqUuix4e1/Lcmen2eTqFY=" />
		</menu:category>
		</security:authorize>
	</menu:menu>
</div>

This will result in different views for different roles:

Admin menu

Admin menu


Menu user

User menu

3 April 2013

Spring Roo 4: User management

Filed under: Java,Spring Roo — Raging Goblin @ 19:48
Tags: ,

Up until now, users are created from a sql script. In this post we are going to provide user management by Roo itself. Role management can be added in the same way if needed, but out of laziness, I won’t show you that.

With the usual ‘web mvc scaffold’ command user management is created by Roo:

focus --class ~.domain.LogUser
web mvc scaffold --class ~.web.LogUserController --backingType ~.domain.LogUser

Unfortunately the views won’t render properly as Spring Roo does not know how to render a LogUserRole to String due to the fact that we did only scaffold the LogUser stuff. If you try to view the LogUser items you will be left with a ‘org.springframework.core.convert.ConverterNotFoundException:’. The ApplicationConversionServiceFactoryBean is responsible for registering formatters, so we add a formatter:

protected void installFormatters(FormatterRegistry registry) {
  super.installFormatters(registry);
  registry.addConverter(getLogUserRoleToStringConverter());
}

public Converter<LogUserRole, String> getLogUserRoleToStringConverter() {
  return new org.springframework.core.convert.converter.Converter<LogUserRole, String>() {
    public String convert(LogUserRole role) {
      return new StringBuilder().append(role.getRoleName()).toString();
    }
  };
}

There is no need to show the password of a LogUser as it is encrypted anyway. So remove it from the views by setting ‘z’ to ‘user-managed’ and ‘render’ to ‘false’ in the same fashion as we did in Spring Roo 1: The basics.

Now, take care that the password gets encrypted before storing it in the database. Copy LogUserController.create from the aspect to the controller and wait a little to let Roo do its household. Then change it to:

@RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(@Valid LogUser logUser, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
  if (bindingResult.hasErrors()) {
    populateEditForm(uiModel, logUser);
  }

  try {
    String hashedPassword = sha256(logUser.getPassword());
    logUser.setPassword(hashedPassword);
    uiModel.asMap().clear();
    logUser.persist();
    return "redirect:/logusers/" + encodeUrlPathSegment(logUser.getId().toString(), httpServletRequest);
  } catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
  } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
  }

  return "logusers/create";
}

private String sha256(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
  MessageDigest digest = MessageDigest.getInstance("SHA-256");
  digest.update(password.getBytes("UTF-8"));
  byte[] hash = digest.digest();
  StringBuffer sb = new StringBuffer();
  for (int i = 0; i < hash.length; i++) {
    sb.append(Integer.toString((hash[i] & 0xff) + 0x100, 16).substring(1));
  }
  return sb.toString();
}

At last, create at least one user (admin/admin) by sql to be able to login:

INSERT INTO `logbook`.`log_user_role` (`id`, `role_name`, `version`) VALUES (NULL, ‘ADMINSTRATOR’, NULL);
INSERT INTO `logbook`.`log_user_role` (`id`, `role_name`, `version`) VALUES (NULL, ‘USER’, NULL);
INSERT INTO `logbook`.`log_user` (`id`, `enabled`, `password`, `username`, `version`) VALUES (NULL, b’1′, ‘8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918’, ‘admin’, NULL);

Remember to not delete all users and you will have a decent way of creating LogUsers.

18 March 2013

Spring Roo 3: Show only items belonging to logged in user

Filed under: Java,Spring Roo — Raging Goblin @ 11:57
Tags: ,

In post 1 we created a simple log application that is capable of storing messages in a database. In post 2 we gained a little privacy with the introduction of Spring Security. However, every post is visible for every logged in user, no matter if you are the creator of that message or not. In this post we are going to remedy that.

In order to show only the posts for the user that is actually logged in, we need 2 things. First we need access to the user that is currently logged in, and second, we need the possibility to search for items belonging to a particular user. To start with the latter, we need finder methods. The command

finder list

will show you the available finder methods. Perform the following commands to add the appropriate finder methods:

focus --class ~.domain.LogUser
finder add findLogUsersByUsernameEquals
focus --class ~.domain.LogItem
finder add findLogItemsByLogUser

If you look into LogUser.java and LogItem.java you will see the finder methods added.

In order to filter for the displayed LogItems we have to move some functionality from the LogItemController_Roo_Controller aspect (LogItemController_Roo_Controller.aj) to the LogItemController_Roo_Controller (LogItemController_Roo_Controller.java). Copy the entire method LogItemController.list from the aspect file to the controller file and rename it to list. Spring Roo will notice this and remove the method from the aspect. We can get the username of the logged in user from the apllication context and use this as starting point to get only the logitems from the logged in user:

@RequestMapping(produces = "text/html")
public String list(@RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "size", required = false) Integer size, Model uiModel) {
   String username = SecurityContextHolder.getContext().getAuthentication().getName();
   List<LogUser> logUserList = LogUser.findLogUsersByUsernameEquals(username).getResultList();
   if(!logUserList.isEmpty()) {
      LogUser user = logUserList.get(0);
      List<LogItem> resultList = LogItem.findLogItemsByLogUser(user).getResultList();
      uiModel.addAttribute("logitems",resultList);
   }
   addDateTimeFormatPatterns(uiModel);
   return "logitems/list";
}

You can now add items till kingdom come but they will never show up in the list because they do not belong to the logged in user. We fix this by moving the method LogItemController.create from the aspect to the controller and changing it to:

@RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(@Valid LogItem logItem, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
    if (bindingResult.hasErrors()) {
        populateEditForm(uiModel, logItem);
        return "logitems/create";
    }
        
    String username = SecurityContextHolder.getContext().getAuthentication().getName();
    List<LogUser> logUserList = LogUser.findLogUsersByUsernameEquals(username).getResultList();
    if(!logUserList.isEmpty()) {
    	LogUser user = logUserList.get(0);
    	logItem.setLogUser(user);
    }
    uiModel.asMap().clear();
    logItem.persist();
    return "redirect:/logitems/" + encodeUrlPathSegment(logItem.getId().toString(), httpServletRequest);
}

8 March 2013

Spring Roo 2: Spring Security with database backend

Filed under: Java,Spring Roo — Raging Goblin @ 11:07
Tags: ,

In the previous post we created a very simple logbook. In this post we will secure it to keep your logs private.

First, we have to expand our database model a little:
Database model
The following commands will adjust our existing application:

entity jpa --class ~.domain.LogUserRole 
field string --fieldName roleName
test integration 
focus --class ~.domain.LogUser
field boolean --fieldName enabled --notNull true
field set --fieldName roles --type ~.domain.LogUserRole

In order to create a secure application Spring Roo comes with a single command:

security setup

Executing this command provides you with all the stuff to lock down your application. However, if you rerun the application you will notice that everything is still open for prying eyes. You have to change the configuration of secure paths in the file applicationContext-security.xml. Change the intercept url patterns to:

<intercept-url pattern="/login"  access="permitAll" />
<intercept-url pattern="/resources/**"  access="permitAll" />
<intercept-url pattern="/**"  access="isAuthenticated()" />

Do not lock everything, because that will lock your login page as well ;). Note the order of the urls! These will be evaluated in the order listed and the first match will be used.

The usernames and passwords are configured in this file as well, and this is definitely not what you want. Normally you keep this stuff in a database. In order to use the LogUser object as credentials there is a little extra work to do.

If you look into LogUser.java you will notice the cascade type. In order to prevent Hibernate from removing the Role when you remove a LogUser, this has to be set to something like Persist:

@ManyToMany(cascade = CascadeType.PERSIST)
private Set roles = new HashSet();

And now comes the magic! Provide a ‘jdbc-user-service’ in the applicationContext-security.xml instead of the ‘user-service’. Replace:

<authentication-provider>
  <password-encoder hash="sha-256" />
  <user-service>
    <user name="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" authorities="ROLE_ADMIN" />
    <user name="user" password="04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb" authorities="ROLE_USER" />
  </user-service>
</authentication-provider>

with:

<authentication-provider>
  <password-encoder hash="sha-256" />
    <jdbc-user-service data-source-ref="dataSource"
      users-by-username-query="SELECT U.username AS username, U.password as password, U.enabled as enabled FROM log_user U where U.username=?"
      authorities-by-username-query="SELECT U.username as username, R.role_name as authority FROM log_user U left join log_user_roles UR on U.id=UR.log_user left join log_user_role R on UR.roles = R.id WHERE U.username=?" />
</authentication-provider>

Change these queries according to your own table- and column names. If you don’t know them, perform the command ‘perform tests’ which will generate the tables so you can put the proper names in the queries.

Last thing to do is insert a user in the database:

INSERT INTO `logbook`.`log_user` VALUES (NULL , 1, '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', 'admin', NULL);

Now the application is only accessible to the user admin with password admin.

28 February 2013

Spring Roo 1: The basics

Filed under: Java,Spring Roo — Raging Goblin @ 10:56
Tags: ,

Previous year I looked into Spring Roo with a couple of colleagues and tried to examine the basics of this framework. In a few posts I want to share what we learned. In this post I will only cover the standard stuff, so nothing outside what is pretty well documented will appear here, but it will provide a foundation for later rants.

We are going to make a very simple web application, a logbook. Users will be able to login and scribble notes in a simple textfield and can attach a date value to it. Sounds simple he? To be honest it is, look at the database model:

Database model

We have a class LogUser with a username and password and a class LogItem with the logmessage and a creationDate. A many to one relationship exists between the Logitem and the LogUser to indicate that every LogItem belongs to one specific LogUser.

Spring Roo comes with a handy feature to run a script. So if you run the following script the application will be created for you in a raw form:

project --topLevelPackage raging.goblin.roo --projectName logbook
jpa setup --database MYSQL --provider HIBERNATE 
properties set --name database.properties --path SPRING_CONFIG_ROOT --key database.url --value jdbc:mysql://localhost:3306/logbook
properties set --name database.properties --path SPRING_CONFIG_ROOT --key database.username --value root
properties set --name database.properties --path SPRING_CONFIG_ROOT --key database.password --value root
entity jpa --class ~.domain.LogItem
field string --fieldName message
field date --fieldName creationDate --type java.util.Date
entity jpa --class ~.domain.LogUser
field string --fieldName username
field string --fieldName password
focus --class ~.domain.LogItem
field reference --fieldName logUser --type ~.domain.LogUser 
test integration 
focus --class ~.domain.LogUser
test integration 
web mvc setup
web mvc scaffold --class ~.web.LogItemController --backingType ~.domain.LogItem

Note the username, password and database connection, alter these to fit to your own settings as these are clearly not meant for production systems ;).

The only thing we have to change is the fact that Spring Roo generated user fields in the views for the LogItems. Now import the contents in Eclipse* as a maven project (file -> import -> existing maven projects -> browse to project folder). Change the rendering of every view residing in ~/workspace/logbook/src/main/webapp/WEB-INF/views/logitems by setting the ‘z’ to ‘user-managed’ and ‘render’ to ‘false’, e.g. in ~/workspace/logbook/src/main/webapp/WEB-INF/views/logitems/show.jspx

<table:column id="c_raging_goblin_roo_domain_LogItem_logUser" property="logUser" z="user-managed" render="false"/>

Run the application with the command ‘mvn jetty:run’ and browse to localhost:8080/logbook:

Logbook

Next time we will explore how to provide security with a login and a database backend.

* This can either be Eclipse with the m2e plugin or the Spring Source Toolsuite as provided by SpringSource.

Create a free website or blog at WordPress.com.