Monday, November 14, 2011

Serving Files in GAE

This post will describe how to serve a file in a Java application hosted in Google App Engine. The sample application provided is based on Vaadin, but the idea can be implemented in any other web framework.


First of all let's see it working.
  1. Click on the application link (gaedownloaddemo.appspot.com)
  2. Use your Google account to log in (don't freak out, you provide your credentials to Google not my application!)
  3. Press the "Download" button to download a simple text file.
If you want to try it out for yourself, the source code is here and can be checked out using this command:
svn co http://tinywebgears-samples.googlecode.com/svn/trunk/gaedownload gaedownload

Now that I have shown you I've done it, let's see what was involved doing so. Before going any further make sure you have a look at Blobstore which might be suitable for you according to your circumstances. There might be other solutions around (like this one), since a couple of months ago when I developed this. You have definitely searched around before coming here.

This can be very easy in a normal Java web application, but you can't use OutputStream in GAE because it is not Serializable. So, starting from a simple Vaadin application for GAE (in this post), I made the following changes:

First of all, I added a new entity called MyFile which has a Blob field to store the file contents:

package com.tinywebgears.gaedownload.core.model;

// Import statements here

@PersistenceCapable(identityType = IdentityType.DATASTORE)
public class MyFile implements Serializable {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;

@Persistent
private String name;

@Persistent
private String username;

@Persistent
Blob file;

@Persistent
private Key ownerKey;

public MyFile() {
}

public MyFile(String name, String username, Blob file) {
this.name = name;
this.username = username;
this.file = file;
}

// Some getters and setters here
}

After that I created a method in the service class (UserService) to create a MyFile and return a special URL as an ExternalResource (We'll see how it is used later):

private void createFile(MyFile file) throws DataPersistenceException
{
file.setOwnerKey(getUserKey());
fileRepo.persist(file);
}

@Override
public Resource serveTextFile(String filename, byte[] text) throws ServiceException
{
try
{
// Create a blob in the database. This is the only
Blob imageBlob = new Blob(text);
MyFile myFile = new MyFile(filename, getUsername(), imageBlob);
createFile(myFile);
Long id = myFile.getKey();
return new ExternalResource(TextFileServlet.SERVLET_PATH + "?" + TextFileServlet.PARAM_BLOB_ID + "=" + id);
}
catch (DataPersistenceException e)
{
throw new ServiceException("An error occurred when downloading the file " + filename + ".", e);
}
}

Then, I served this ExternalResource in a new window somewhere in the Vaadin code:

Resource resource = userServices.serveTextFile(fileName, fileContents.getBytes());
// Redirecting to servlet in order to download the file
mainWindow.open(resource, "_blank");

Now, the application needs to know how to serve this URL. For this purpose I add a new Servlet:

<servlet>
<servlet-name>FileServerServlet</servlet-name>
<servlet-class>com.tinywebgears.gaedownload.servlet.TextFileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileServerServlet</servlet-name>
<url-pattern>/gaedownload/servefile</url-pattern>
</servlet-mapping>

The Servlet's code looks like this:

package com.tinywebgears.gaedownload.servlet;

// Import statements here

public class TextFileServlet extends HttpServlet
{
public static final String PARAM_BLOB_ID = "id";
public static final String SERVLET_PATH = "/gaedownload/servefile";

private final Logger logger = LoggerFactory.getLogger(TextFileServlet.class);

@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException
{
Principal userPrincipal = req.getUserPrincipal();
PersistenceManager pm = PMFHolder.get().getPersistenceManager();
Long id = Long.parseLong(req.getParameter(PARAM_BLOB_ID));
MyFile myfile = pm.getObjectById(MyFile.class, id);

if (!userPrincipal.getName().equals(myfile.getUserName()))
{
logger.info("TextFileServlet.doGet - current user: " + userPrincipal + " file owner: "
+ myfile.getUserName());
return;
}

res.setContentType("application/octet-stream");
res.setHeader("Content-Disposition", "attachment;filename=\"" + myfile.getName() + "\"");
res.getOutputStream().write(myfile.getFile().getBytes());
}
}

You can always check the complete code if I have missed something here. I like this approach, because you don't need to create any HTML to upload into Blobstore. I hope you also find it useful.

Thursday, September 8, 2011

Simple Charts in GWT using Google Charts API

I recently started to muck around with Google Charts API to generate financial charts. My aim is to be able to generate a price chart annotated with news and company announcement information, something like this (click to see it clear):


What I have got so far is a timeline chart in a GWT application. This simple application will chart the price information of a few stocks provided in a CSV file.

The code is here, and can be checked out using this command:
svn co http://tinywebgears-samples.googlecode.com/svn/trunk/simplecharts simplecharts
It is an Eclipse project you can use easily if you too use Eclipse.

The class responsible to generate this chart looks like this:

// ...

// Entry point class
public class SimpleCharts implements EntryPoint
{
// ...

// Entry point method
@Override
public void onModuleLoad()
{
// ...

// Load the visualization API, passing the onLoadCallback to be called when loading is done.
VisualizationUtils.loadVisualizationApi(onLoadCallback, AnnotatedTimeLine.PACKAGE);

// ...
}

// ...

private void makeChartDialog(Map<Date, Map<String, Double>> rawData)
{
AnnotatedTimeLine timeLine = new AnnotatedTimeLine(createDataTable(rawData), createOptions(), "600px", "400px");

chartDialogBox.setAnimationEnabled(true);

VerticalPanel vp = new VerticalPanel();
vp.addStyleName("dialogVPanel");

VerticalPanel vinp = new VerticalPanel();
vinp.addStyleName("dialogVInnerPanel");

vinp.add(timeLine);

vp.setHorizontalAlignment(VerticalPanel.ALIGN_LEFT);
vp.add(vinp);
vp.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
vp.add(closeButton);

chartDialogBox.setWidget(vp);

// Add a handler to close the DialogBox
closeButton.addClickHandler(new ClickHandler()
{
public void onClick(ClickEvent event)
{
chartDialogBox.hide();
}
});
}

private void paintChart(String stockCodeList)
{
String[] stockCodes = StringHelper.separateCodes(stockCodeList);
ausequitiesService.getPriceInfo(stockCodes, new AsyncCallback<Map<Date, Map<String, Double>>>()
{
public void onFailure(Throwable caught)
{
makeErrorMessageDialog(SERVER_ERROR);
errorDialogBox.center();
chartButton.setEnabled(true);
closeButton.setFocus(true);
}

public void onSuccess(Map<Date, Map<String, Double>> result)
{
makeChartDialog(result);
chartDialogBox.center();
chartButton.setEnabled(true);
closeButton.setFocus(true);
}
});
}

private Options createOptions()
{
AnnotatedTimeLine.Options options = AnnotatedTimeLine.Options.create();
options.setDisplayAnnotations(true);
options.setDisplayZoomButtons(true);
options.setScaleType(AnnotatedTimeLine.ScaleType.ALLFIXED);
options.setLegendPosition(AnnotatedTimeLine.AnnotatedLegendPosition.SAME_ROW);
return options;
}

private DataTable createDataTable(Map<Date, Map<String, Double>> rawData)
{
DataTable data = DataTable.create();
data.addColumn(AbstractDataTable.ColumnType.DATE, "Date");
if (rawData.isEmpty())
return data;
for (Entry<Date, Map<String, Double>> entry : rawData.entrySet())
{
for (String code : entry.getValue().keySet())
data.addColumn(AbstractDataTable.ColumnType.NUMBER, code);
break;
}
Integer counter = 0;
for (Entry<Date, Map<String, Double>> entry : rawData.entrySet())
{
data.addRows(1);
data.setValue(counter, 0, entry.getKey());
Integer index = 1;
for (Double price : entry.getValue().values())
{
if (price != null)
data.setValue(counter, index, price);
index++;
}
counter++;
}
return data;
}
}

I have removed less related lines for the sake of brevity, but you can see the complete file following the link mentioned above.

You can see the deployed version of application here on App Engine. It will be updated as I make changes to the code in the future.

Here is what I have been to plot so far:

Monday, August 22, 2011

Configuring a Web Application in Tomcat

Suppose you have a web application configured for Tomcat using a context configuration file (META-INF/context.xml), as I mentioned in this post. You read Tomcat's documentation saying (not exactly in these words though):
If your .war file contains META-INF/context.xml it will be copied into $CATALINA_HOME/conf/[enginename]/[hostname]/[appname].xml and if there is such a file it won't be overwritten.

You happily created .war packages and shipped to the production, just to start receiving complaints about context configuration being overwritten. This is when you go and read another part of Tomcat's documentation saying:
An update to a WAR which has been expanded will trigger an undeploy (with a removal of the expanded webapp), followed by a deployment.

If we combine these two together, we can conclude that: If you install an application via copying a .war file into the webapp folder, then you are gonna end up losing changes made to the context configuration. There is a bug report associated with this problem, which is marked as WON'T FIX by Tomcat development team. So, if you still insist not to use Application Server's administrative interface, keep reading for a possible fix.

What I have come up with is this:

1- Install an exploded WAR directory somewhere outside Tomcat's appBase, let's assume it is in /usr/local/MyApp. 2- Copy the context configuration file into [tomcat.conf]/[engine]/[hostname] directory, let's call it MyApp.xml. This file will point to the location of the application:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Context configuration file for my web application -->
<Context docBase="/usr/local/MyApp" privileged="true" antiResourceLocking="false" antiJARLocking="false">
<Resource name="jdbc/myapp-ds" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="XXX" password="XXX" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/mydb" />
</Context>


3- You are now free to go and modify the configuration file.

4- Update the application by copying new version of your application in /usr/local/MyApp

Notes:

a) This solution applies to an unexpanded .war file, but since we use Spring's Log4JConfigListener it wouldn't run from an unexploded .war file. Tomcat doesn't explode .war files put outside appBase (webapps) folder.

b) This approach doesn't prevent you from having context.xml in /usr/local/MyApp/META-INF/context.xml since it will not be used by Tomcat in this configuration. You can use it in your dev environment, where you dump your .war file into the appBase (webapps) folder.

I am still looking out for simpler solutions, though.

Thursday, August 18, 2011

A simple Vaadin application for Google App Engine

Here is a very simple application written in Vaadin that can be installed on Google App Engine. This application uses the users API to log users in and out. It also saves users last login time in the database as an example on how to connect to database. It is built by Maven and separated in two different modules, Java codes and GAE stuff.

Browse the code here, or check it out on your machine using this command:
svn co http://tinywebgears-samples.googlecode.com/svn/trunk/simplegae simplegae

These are the steps to get it working on your own box, as outlined in the README file:
  1. Run install-gae-sdk.sh in order to install appengine-local-runtime (not included in Maven repository).
  2. Run mvn clean install to compile the application.
  3. Run run-gae.sh to run the application locally.
  4. Create a new AppEngine application (here).
  5. Modify ./simplegae-appengine/src/main/webapp/WEB-INF/appengine-web.xml and put in name and version number of your application.
  6. Run deploy-gae.sh to deploy the application.
The provided scripts shouldn't put Windows users off, since they only run a sequence of simple commands.

One of the biggest impacts of moving to App Engine on your Vaadin application is the fact that your application will end up being serialized/deserialized between requests. As a prerequisite, all your classes must be Serializable. Unsupported features (like polymorphic queries) and some specific requirements (transactions and entity groups) should also be considered.

Sunday, July 17, 2011

Custom Authentication using Spring Security

Today I will describe how to use Spring Security (3.0.5) to implement a form-based authentication using your own authentication service (well, one of the ways I did so!). I will show you a sample web application with a dummy authentication and remember-me (SSO) service in this post.
The source code for this sample can be found here, or checked out on your own machine using this command:
svn checkout http://tinywebgears-samples.googlecode.com/svn/trunk/customauth customauth

To run the application you could either compile it with maven or import it in your favourite IDE, then deploy the WAR file in a Servlet container. The web application will be available on http://host:port/customauth/ url (replace host and port with your own).The dummy authentication service has two user accounts you might use to log in: user/user which is a normal user, and admin/admin which is an administrator. The way that dummy SSO works is a bit funny though, I couldn't think of a better way of doing so without storing user sessions: If you have an authenticated session and restart your application (by either redeploying it or restarting the container) you have 30 seconds to re-use your SSO session, otherwise you have to log in again.

Our sample application has a few XML files (web.xml, customauth-servlet.xml, applicationContext.xml, and applicationContext-security.xml) to specify how the web application and Spring beans are configured. Lets have a look at a big picture to see what we are going to achieve using these configuration files (click for the big image):


I had to implement four classes (highlighted in the diagram), one enum for the role, and one controller class. Please browse the source code for more details, they are too big to be shown here. Now we will take a peek inside configuration files.

web.xml configures the web application and servlets.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">

<display-name>Spring security web application (series)</display-name>

<!-- to specifically stop trouble with multiple apps on tomcat -->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>customauth_root</param-value>
</context-param>

<!-- Location of the XML file that defines the root application context
applied by ContextLoaderListener. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
   WEB-INF/applicationContext.xml
   WEB-INF/applicationContext-security.xml
     </param-value>
</context-param>

<!-- Loads the root application context of this web app at startup. The
application context is then available via WebApplicationContextUtils.getWebApplicationContext(servletContext). -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Provides core MVC application controller. See customauth-servlet.xml. -->
<servlet>
<servlet-name>customauth</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>customauth</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>customauth</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>


customauth-servlet.xml configures Spring MVC controllers.
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
 http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd
 ">

<!-- no 'id' required, HandlerMapping beans are automatically detected by
the DispatcherServlet -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
 <prop key="/*.htm">urlController</prop>
 <prop key="/*.do">actionController</prop>
</props>
</property>
</bean>

<bean id="actionController"
class="com.tinywebgears.samples.customauth.controller.ActionController" />

<bean id="urlController"
class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />

<!-- as we have our jsp in an internal place forcing all requests through
spring, use viewResolver to save us making reference to internal structure
everywhere e.g. /WEB-INF/jsp/ -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="attributes">
<props>
 <prop key="message">Static Message</prop>
</props>
</property>
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>

</beans>


applicationContext.xml configures the <sec:http> tag.
<?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:sec="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
   ">

<sec:http auto-config="false" entry-point-ref="authenticationEntryPoint">
<sec:custom-filter position="REMEMBER_ME_FILTER"
ref="rememberMeFilter" />
<sec:custom-filter position="FORM_LOGIN_FILTER"
ref="umsAuthenticationProcessingFilter" />
<sec:custom-filter position="LOGOUT_FILTER" ref="umsLogoutFilter" />

<sec:intercept-url pattern="/login.jsp" filters="none" />
<sec:intercept-url pattern="/denied.jsp" filters="none" />
<sec:intercept-url pattern="/admin.htm" access="ROLE_ADMIN" />
<sec:intercept-url pattern="/**" access="ROLE_USER" />

<sec:access-denied-handler ref="accessDeniedHandler" />
</sec:http>

<bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/denied.jsp" />
</bean>
</beans>


applicationContext-security does the main job by wiring all those things you saw in the diagram together.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
   ">

<global-method-security secured-annotations="disabled" />

<beans:bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="location">
<beans:value>classpath:ums.properties</beans:value>
</beans:property>
</beans:bean>

<!-- Filters -->

<beans:bean id="rememberMeFilter"
class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<!-- custom-filter position="REMEMBER_ME_FILTER"/ -->
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="rememberMeServices" ref="umsRememberMeServices" />
</beans:bean>

<beans:bean id="umsLogoutFilter"
class="com.tinywebgears.samples.customauth.service.UmsLogoutFilter">
<!-- custom-filter position="LOGOUT_FILTER" / -->
<beans:constructor-arg value="/login.jsp?loggedout=true" />
<beans:constructor-arg>
<beans:list>
 <beans:ref bean="umsRememberMeServices" />
 <beans:bean id="logoutHandler"
  class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
 </beans:bean>
</beans:list>
</beans:constructor-arg>
<beans:property name="cookieName" value="${Ums.SSO.Cookie.Name}" />
<beans:property name="filterProcessesUrl" value="/j_spring_security_logout" />
</beans:bean>

<beans:bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login.jsp" />
</beans:bean>

<beans:bean id="umsAuthenticationProcessingFilter"
class="com.tinywebgears.samples.customauth.service.UmsAuthenticationProcessingFilter">
<beans:property name="cookieName" value="${Ums.SSO.Cookie.Name}" />
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="umsUserDetailsService" ref="umsUserDetailsService" />
<beans:property name="authenticationSuccessHandler">
<beans:bean id="authenticationSuccessHandler"
 class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"
 p:alwaysUseDefaultTargetUrl="false" p:defaultTargetUrl="/home.htm" />
</beans:property>
<beans:property name="authenticationFailureHandler">
<beans:bean id="authenticationFailureHandler"
 class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
 p:defaultFailureUrl="/login.jsp?authfailed=true" />
</beans:property>
</beans:bean>


<!-- Authentication Manager -->

<!-- This will override the settings of authentication manager bean. -->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="umsUserDetailsService">
<password-encoder hash="sha" base64="true" />
</authentication-provider>
<authentication-provider ref="rememberMeAuthenticationProvider" />
</authentication-manager>


<!-- Beans and Providers -->

<beans:bean id="umsUserDetailsService"
class="com.tinywebgears.samples.customauth.service.UmsUserDetailsService">
</beans:bean>

<beans:bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<!-- This ensures that remember-me is added as an authentication provider -->
<beans:property name="key" value="${Ums.SSO.Cookie.Key}" />
</beans:bean>

<beans:bean id="umsRememberMeServices"
class="com.tinywebgears.samples.customauth.service.UmsRememberMeServices">
<beans:property name="userDetailsService" ref="umsUserDetailsService" />
<beans:property name="cookieName" value="${Ums.SSO.Cookie.Name}" />
<beans:property name="key" value="${Ums.SSO.Cookie.Key}" />
</beans:bean>

</beans:beans>


You can get this code as a starting point and modify those classes to use your desired authentication service.

Note: Spring security 3.0.4 which I was using first has some bugs related to sec:http tag which prevented forwarding to error pages properly. Please note that error pages are forwarded to, not redirected to (see here if you don't know the difference). It is strongly recommended to update your libraries if you are having issues.

Friday, July 1, 2011

Spring + Configuration

A real world web application (like any other application) needs to be configured somehow. Sometimes this configuration can be known in the compile-time, but some configuration will be provided by an administrator later.

In order to demonstrate two examples of configuration, I am going to pick the application you saw this post (XML-RPC sample) and modify some parts of it to:
1- Populate the properties files using Maven in the compile-time. Here I will use a plug-in to read SVN meta-data and pass the revision number to the properties files.
2- Read the application's dynamic configuration using JNDI. A JDBC datasource is a very good example.
And, I am not going to write any new code to do so.

There is a full working code for this blog post in here that can be checked out using this command:
svn checkout http://tinywebgears-samples.googlecode.com/svn/trunk/contextsample contextsample

1- Populate the properties files:

First of all we are going to have a properties file to define all we need in (contextsample-services.properties):
# These properties are defined in the master pom.xml file
product.name: ${mavenproperties.product.name}
product.version: ${mavenproperties.product.version}
# These properties are populated by maven-svn-revision-number-plugin
product.svn.revision: ${svnpluginproperties.revision}


Then, we need to ask Maven to filter the resource fiels, so that those ${param} placeholders are replaced by actual values.
     <resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>


We have defined some parameters in the pom.xml file that will be used to filter properties files:
<properties>
<!-- Properties exposed by the application -->
<mavenproperties.product.name>${name}</mavenproperties.product.name>
<mavenproperties.product.version>${version}</mavenproperties.product.version>
</properties>


We also need to use this plug-in to read the SVN meta-data. This plug-in will provide svnpluginproperties.* properties accessed in our configuration.

         <plugin>
<groupid>com.google.code.maven-svn-revision-number-plugin</groupid>
<artifactid>maven-svn-revision-number-plugin</artifactid>
<version>${revisionNumberPluginVersion}</version>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<entries>
<entry>
<prefix>svnpluginproperties</prefix>
</entry>
</entries>
</configuration>
</plugin>


Now, if you compile your code you will have a contextsample-services.properties file (in the target path) that has the right values for parameters. You might want to read this file in your code, or in your Spring configuration file using a tag like this:

 <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:contextsample-services.properties</value>
</property>
</bean>


This way you can access the values of those parameters as ${product.name}, ${product.version}, and ${product.svn.revision} in the bean definitions.

Please note that the properties file we ended up with will be bundled in the application and is not a good place to put your dynamic configuration, because no one will be able to modify them easily after you made the project artifacts.

2- Read dynamic configuration

There are different ways to achieve this goal, but in this post you will see a very easy way of doing so using JNDI. One advantage of it is that you don't need to declare the path of configuration file anywhere in the application. If your application has so many configuration parameters it is possible to declare the path to a properties file (including those parameters) this way and read the whole file in the way mentioned above. Your application is tied to name of JNDI paremeters which is not a big deal.

You don't need a full J2EE application server to do this since most of the Servlet containers have this capability. The instructions provided here will work on Tomcat 5+, and it is very likely to be different for other application servers.

First of all you need to create a META-INF/context.xml file containing these lines:

<!--?xml version="1.0" encoding="UTF-8"?-->

<!-- Context configuration file for the Data Library XML-RPC services -->

<context docbase="${catalina.home}/webapps/contextsample-services" privileged="true" antiresourcelocking="false" antijarlocking="false">

<!-- Define parameters -->
<environment name="config/environment" value="prod 1" type="java.lang.String" override="true">

</environment></context>


Then, all you need to do is reference the parameters provided by JNDI in your Spring context configuration file:

 <bean id="sampleService" class="com.tinywebgears.samples.service.SampleServiceImpl">
<constructor-arg index="0">
<jee:jndi-lookup name="config/environment">
</jee:jndi-lookup></constructor-arg>
...
</bean>


The trick is that Tomcat will get this context.xml file and copy it somewhere in the configuration directory so that it can be edited easily by the administrator. If the file is already there it won't be overwritten, to avoid accidentally overriding hard work of administrators. Please read this section from Tomcat docs for more details. Here is an excerpt from that document:

Only if a context file does not exist for the application in the <code>$CATALINA_HOME/conf/[enginename]/[hostname]/</code>; in an individual file at <code>/META-INF/context.xml</code> inside the application files. If the web application is packaged as a WAR then <code>/META-INF/context.xml</code> will be copied to <code>$CATALINA_HOME/conf/[enginename]/[hostname]/</code> and renamed to match the application's context path. Once this file exists, it will not be replaced if a new WAR with a newer <code>/META-INF/context.xml</code> is placed in the host's appBase.

Please note that providing an <environment> tag in the context.xml is equivalent of providing an <env-entry> tag in the web.xml file. If you provide both you will probably override your application context configuration file.

UPDATE: If you deploy your application by dumping WAR file in Tomcat's webapps folder, this does not apply to you. Tomcat will undeploy your application first (removing the context configuration as well, dropping all the changes you might have done to it). See this post for a solution.

Friday, June 24, 2011

Single Threading in Vaadin!

Are you developing a Vaadin application for Google App Engine (or you might not be able to spawn threads for any other reason)? Then this post will help you out in creating a more responsive application. This will especially be helpful if you are trying to do heavy backend tasks (Java code as opposed to the client-side JavaScript code). Well, consider it heavy in GAE terms if it is going to take more than 30 seconds.

What to achieve:
I want to update the UI accordingly during a long backend process (e.g. by updating a progress indicator) and let the user intervene to stop it.

The problem:
Without spawning another thread, modifications to the UI components will not take effect until the backend process finishes.

Assumption:
I will try to use Vaadin's capabilities rather than locking myself into Google's task queue API.

What I have done is creating a custom Echo component that simulates a backend to frontend round trip. I have broken down the long process into small chunks. Now I need to call this component from my backend code after doing each chunk of operation. Echo goes off and returns, on return I will resume the process.

Another place this component can be useful is where you want to disable a button while the handler is still working. This could be achieved easily using another thread, but if you cannot spawn threads this is how you might do it: On the button's click listener disable the button and then send an echo. The button is disabled now and you will continue your process in the backend on the response.

There is complete source code available for the custom component and also a sample application that shows how it can be used. Use this command to check it out on your own machine:

svn checkout http://tinywebgears-samples.googlecode.com/svn/trunk/echosample/ echosample-read-only
or feel free to browse the repository (here).You can use the jar files provided right away or compile it yourself.

Update: If you seek this solution only to disable a button while processing the click, be aware of this change to the Button that will enable you to call button.setDisableOnClick(false). The feature is to be added to Vaadin 6.7 and you might try the nightly build for an early access.

Monday, June 13, 2011

XML-RPC + Maven + Spring

I was lately trying to implement a basic set of services in XML-RPC. Well, it had to be XML-RPC as a architectural design constraint if you are interested why . Other than that I had other requirements as well: It had to be on Spring, must be built by Maven, and most importantly it had to be a WAR file in a container not a standalone application.

Googling around, I came up with a few blog posts providing hints and code snippets. Here are some of them that I actually based my code on:
Salmon Run (07 Feb 2010)
Tomas Salfischberger (28 March 2008)
Perry Nguyen (6 Oct 2006)
Here I am not adding much to what these guys have already said, but instead doing what they could have done to save me hours of trial and error: Providing a complete working example.

The sample application provided here is a Spring based web application that can be hosted in an application server like Tomcat.

You can browse the code in this SVN repository. Alternatively you might try this command to check it out completely from repository:
svn checkout http://tinywebgears-samples.googlecode.com/svn/trunk/xmlrpcsample/ xmlrpcsample

This application also provides a sample Java client that can be used to call the sample service. The client code connects to the server at this pre-configured location:
http://127.0.0.1:8080/xmlrpc-backend-services/xmlrpc/
and then invoke the sample service called 'sampleService.getVersion'.
You might need to change the client code if you deploy in a different configuration.

For those who are intersted, I have also been able to add another view controller and other set of Spring MVC views to serve a web application living besides the XML-RPC services, but I removed those bits in order to keep it simple.

The parts of configuration that tend to go wrong (and took me bloody hours to get them fixed) are web.xml and Spring's beans configuration file. Here is my web.xml file:

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/META-INF/properties/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

<!-- Spring context -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:WEB-INF/spring/*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Spring webapp -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- Welcome file -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>

</web-app>



And finally the beans configuration will be as follows:

<?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"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">


<!-- XML-RPC -->

<!-- XML-RPC Services -->
<bean id="sampleService" class="com.tinywebgears.samples.service.SampleServiceImpl">
</bean>
<!-- Processor Factory -->
<bean id="requestProcessorFactory"
class="com.tinywebgears.samples.xmlrpc.ConfiguredBeanProcessorFactory">
<property name="classBeanMap">
<map>
<entry key="com.tinywebgears.samples.service.SampleService"
value-ref="sampleService" />
</map>
</property>
</bean>
<!-- Controller -->
<bean id="xmlRpcHandler" class="com.tinywebgears.samples.xmlrpc.XmlRpcServerController">
<property name="factory" ref="requestProcessorFactory" />
<property name="mappings">
<props>
<prop key="sampleService">com.tinywebgears.samples.service.SampleService</prop>
</props>
</property>
</bean>


<!-- Dispatcher -->

<bean id="simpleUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<property name="mappings">
<props>
<prop key="/xmlrpc/*">xmlRpcHandler</prop>
</props>
</property>

</bean>

<context:component-scan base-package="com.tinywebgears.samples" />

</beans>

Friday, June 10, 2011

Beans Go Crazy

After years of contemplation (!) I finally found the guts to start my techie blog.
Here I will be writing about my software development experiences and provide useful tips and code snippets, like what everyone else does.
You might then ask "why bother"? Yes, you're right. There is something different here. I will provide complete working code if what I talk about is in the code level and more than a simple tip.
Needless to say that Java will be the dominant topic of this blog, well that's why it is called 'Beans Go Crazy', because they most of the times do!
Although every other sensible names I came up with had already been registered a few years ago, having only one 'Hello World' post.


Talk to you soon.