Raging Goblin

29 May 2014

KNMI Rain radar app for Android

Filed under: Android — raginggoblin @ 20:22
Tags:

On my telephone I wanted a very simple rain radar app. The Android appstore provide tons of weather apps, but I could not find a simple rain radar viewer without any bells or whistles at all. I do not care about all these nice features and especially hate the meaningless predictions of these apps. The combination of my eye and brain does a much better prediction than some simple pixel shifting algorithm. Besides this, it has the extra benefit of no advertisements cluttering my screen! Look here for details.

20 March 2014

Search Java SE API

Filed under: Java — raginggoblin @ 20:27
Tags: , ,

For many years Sun and Oracle have failed to add a searchfield to the Javadocs found on http://docs.oracle.com/javase/7/docs/api/. Most of the time I use the Zeal API browser to search the Javadocs. But sometimes it is easier to let Google do the searching for you. To add a ‘site-specific’ search engine to Firefox add the following xml to a file in your profile directory inside the directory searchengines or searchplugins:

<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
                       xmlns:moz="http://www.mozilla.org/2006/browser/search/">
  <ShortName>Java SE7 API</ShortName>
  <Description>Search Java Standard Edtition API with Google</Description>
  <InputEncoding>UTF-8</InputEncoding>
  <Image width="16" height="16" type="image/x-icon">data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAHBPoACxfnAEtN7wCHhv4AAAL9AENF8QD29f8AwML/AAQA8wCHhf8AAAH+ABkT6ADv7v8AJiDpAAYE+ADx7v8A0tb/AAEJ5gDv8f8AAAT3APnu/wAAAvsAT1PnAAYD+QAIBPcADgf4AAoE9wACBP0AAQjnAPb0/wAAA/gAAgX0AAoM5gAAAvoABQP/AAAA/gACAvoAAgD+ANHV/wADAP4A6PD/AIiH/wDs8P8AAAP3ACUf6AADAfsARkj0AGBk8gCcnv8AAAH6AAcF+QAAAPwABwbwAOfv/wBOUuYACQbwAFdU8ACGhv8AAAP8ABER5wCIhv8AAAL+AHx59wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALSclJSETEx4THhMkJSclLSMVEwQKCgoKCgoKCgQTFSMECgoVHzQ3Nzc3NB8VCgoEKzMZDRY5KQkDPDkWDRkzKyI7LyYPKig1NSgqDyYvOyIIOBIMMAIuBQUuAjAMEjgIIAcGPgsaDgAyFxgLPgYHIBEUHQEEGzozMT0bBAEdFBwRFB0BBBs6MzE9GwQBHRQcIAcGPgsaDgAyFxgLPgYHIAg4EgwwAi4FBS4CMAwSOAgiOy8QDyooNTUoKg8QLzsiKzMZLDY5KQkDPDk2LBkzKwQKChUfNDc3Nzc0HxUKCgQjFRMECgoKCgoKCgoEExUjLSclJSETEx4THhMkJSclLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= </Image>
  <Url type="text/html" method="GET" template="http://www.google.com/search">
    <Param name="q" value="{searchTerms}"/>
    <Param name="sitesearch" value="http://docs.oracle.com/javase/7/docs/api/"/>
  </Url>
  <moz:SearchForm>http://www.world-connect-commodities.ro:8775/Search/default.aspx</moz:SearchForm>
</OpenSearchDescription>

For your convenience, you can download the file from here as well. After restarting Firefox you get a nice Javadoc search engine:
Site specific search

30 November 2013

Infolog version 4.0 released

Filed under: Android,Egroupware,Java — raginggoblin @ 22:01
Tags: , ,

Infolog version 4.0 is released! The communication between the client and the server now uses a Rest webservice. This enabled me to write an Android client as wel. When upgrading, please note that data is now stored in json format. Before upgrading, make sure all your notes are on the server. See Infolog page for details about the program.

17 July 2013

Spring Roo 10: Update images

Filed under: Java,Spring Roo — raginggoblin @ 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.

16 May 2013

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

Filed under: Java,Spring Roo — raginggoblin @ 20:39

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 — raginggoblin @ 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 — raginggoblin @ 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 — raginggoblin @ 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 — raginggoblin @ 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.

Next Page »

The Rubric Theme. Blog at WordPress.com.