easy, elegant and effective code…

I am writing this post to share the solution for various issues I came across while testing AWS IoT using MQTT.fx Client.

In this post, I am going to show various configuration required on MQTT.fx client side as well as on AWS IoT side. I have done my testing with the MQTT.fx verion – 1.3.1 (on Windows 7, 64 bit) but the steps/configuration I have mentioned in this post should work with other versions of the MQTT.fx clients as well.

First, you need create a Device (Thing), a Policy and a Certificate as per the instructions given in AWS IoT Developer Guide. Here are the links, you can refer –

Create Thing
http://docs.aws.amazon.com/iot/latest/developerguide/register-device.html

Create and Activate a Device Certificate
http://docs.aws.amazon.com/iot/latest/developerguide/register-device.html
NOTE: Download the certificate for the thing, root CA and private key (important). You cannot retrieve the private key later.

certdownload

Attach an AWS IoT Policy
http://docs.aws.amazon.com/iot/latest/developerguide/attach-iot-policy.html

At this point, you should have newly created a Thing, a Policy and a Certificate created (Make sure the thing and the policy is attached to the certificate).

Now, open the MQTT.fx client and create a new connection profile by going to the following menu option –
Extras -> Edit Connection Profiles
and then clicking the ‘+’ button (at bottom left side).

Find the broker address for your device (thing) by selecting your device/thing in the AWS IoT console and then clicking on ‘Interact’ menu.
(AWS IoT -> Registry -> Things -> THING_NAME -> Interact)

awsiothostname

The Rest API endpoint name under HTTPS section is your broker address.

Port Number for the secured MQTT connection is 8883.

You can specify any value for the ‘Profile Name’ and the ‘Client ID’ fields.

In the lower tabs, only change the values for various fields in the ‘SSL/TLS’ tab and leave other tabs with the default value. Configure the values in the ‘SSL/TLS’ tab as shown in the screen shot below and then click on the ‘Apply’/’OK’ button.

mqttfxconf

If everything goes fine, hopefully you should be able to connect successfully.

mqttfxconn

However, in case of any issue in connection, click on the ‘Log’ tab to see the logs.

If you come across ‘certificate_unknown‘ error as following –

org.eclipse.paho.client.mqttv3.MqttException: MqttException
	at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:664) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	at java.lang.Thread.run(Unknown Source) [?:1.8.0_112]
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
	at sun.security.ssl.Alerts.getSSLException(Unknown Source) ~[?:1.8.0_112]
	at sun.security.ssl.Alerts.getSSLException(Unknown Source) ~[?:1.8.0_112]
	at sun.security.ssl.SSLSocketImpl.recvAlert(Unknown Source) ~[?:1.8.0_112]
	at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source) ~[?:1.8.0_112]
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source) ~[?:1.8.0_112]
	at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) ~[?:1.8.0_112]
	at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) ~[?:1.8.0_112]
	at org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:93) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:650) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]

Solution: Check if your certificate is enabled or not. If you have configured the correct certificates as shown before, enabling the certificate should resolve the issue.

If you encounter ‘Connection lost‘ issue with the error logs as follows –

org.eclipse.paho.client.mqttv3.MqttException: Connection lost
	at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:146) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	at java.lang.Thread.run(Unknown Source) [?:1.8.0_112]
Caused by: java.io.EOFException
	at java.io.DataInputStream.readByte(Unknown Source) ~[?:1.8.0_112]
	at org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream.readMqttWireMessage(MqttInputStream.java:65) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:107) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	... 1 more
ERROR --- MqttFX ClientModel             : Please verify your Settings (e.g. Broker Address, Broker Port & Client ID) and the user credentials!
org.eclipse.paho.client.mqttv3.MqttException: Connection lost
	at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:146) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	at java.lang.Thread.run(Unknown Source) [?:1.8.0_112]
Caused by: java.io.EOFException
	at java.io.DataInputStream.readByte(Unknown Source) ~[?:1.8.0_112]
	at org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream.readMqttWireMessage(MqttInputStream.java:65) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:107) ~[org.eclipse.paho.client.mqttv3-1.1.0.jar:?]
	... 1 more

Solution: Check if the policy is attached to the certificate or not.

If policy is also attached to the certificate, then edit the policy as follows –

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": "*"
    }
  ]
}

This should resolve the issue.

Leave your comments, if you still have any issue in the MQTT connection.

Advertisements

Given below is the sample java implementation of the “Abstract Factory” design pattern where we are instantiating the object of widgets (window/button) of a required family (motif or mac).
We need to select a factory of specific family once only …then we automatically get all the objects of the specified family; we need not specify the family every time …we want to instantiate an object.

interface XWindow
{ 
}

class MotifWindow implements XWindow
{
   public MotifWindow()
   {
      System.out.println("MotifWindow Created.");
   }
}

class MacWindow implements XWindow
{
   public MacWindow()
   {
      System.out.println("MacWindow Created.");
   }
}

interface XButton
{
   
}

class MotifButton implements XButton
{
   public MotifButton()
   {
      System.out.println("MotifButton Created.");
   }
}

class MacButton implements XButton
{
   public MacButton()
   {
      System.out.println("MacButton Created.");
   }
}

interface WidgetFacotry
{
   XWindow createXWindow();
   XButton createXButton();
}

class MotifWidgetFactory implements WidgetFacotry
{

   @Override
   public XButton createXButton()
   {
      return new MotifButton();
   }

   @Override
   public XWindow createXWindow()
   {
      return new MotifWindow();
   }
  
}

class MacWidgetFactory implements WidgetFacotry
{

   @Override
   public XButton createXButton()
   {
      return new MacButton();
   }

   @Override
   public XWindow createXWindow()
   {
      return new MacWindow();
   }   
}

public class AbstractFactory
{
   public static void main(String args[])
   {
      WidgetFacotry factory = new MacWidgetFactory();
      //WidgetFacotry factory = new MotifWidgetFactory();
      
      factory.createXWindow();
      factory.createXButton();      
   }   
}

On executing the above program, you are going to get the following output –

MacWindow Created.
MacButton Created.

If you comment the line #85 and un-comment the line #86, you will get the following output –

MotifWindow Created.
MotifButton Created.

A pattern is a type of theme of recurring events or objects. So the commonly or repeatedly occurring problems or requirements also has some pattern. These problems/requirements can be in any of the domain e.g. in engineering, medical science etc. Since the problem/requirement is recurring and many people have encountered the same, chances are that someone has already solved that problem for you and you need not to re-invent the wheel. Solution to the repeatedly occurring problems is called Design Pattern.

It would be expeditious if we don’t have to re-invent the wheel.

Software Design Pattern is a general repeatable solution to a commonly occurring problems in software engineering. A design pattern is well documented template which describes the problem, the solution, when to apply the solution and its consequences.

Following are some of the benefits of using design patterns-

  • Design patterns can speed up the development process by providing tested, proven development paradigms.
  • Design reuse.
  • Common vocabulary.
  • Providing the reference to a specific design pattern can save lots of documentation efforts.

Design patterns are mainly grouped into three categories –

  • Creational Design Pattern
  • Structural Design Pattern
  • Behavioral Design Pattern
Creational Design Pattern

A creational design pattern (as it’s name suggests) maily deals with object creation.

e.g. – Abstract Factory, Builder, Factory Method, Singleton, Prototype etc.

Creational patterns can further devided into two classes – Object Creational Pattern and Class Creational Pattern.

Object Creational Pattern

Object Creational Patterns use delegation in the instantiation process.

Class Creational Pattern

Class Creational Patterns use inheritance in instantiation process thus defers its object creation to subclass.

Structural Design Pattern

A structural design pattern uses composition to merge objects and classes into larger structures. They show you how to glue different pieces of a system together in a flexible and extensible fashion. Structural design patterns help you guarantee that when one of the parts changes, the entire structure does not need to change. They also show you how to recast pieces that do not fit (but that you need to use) into pieces that do fit.

e.g. – Adapter, Bridge, Composite, Decorator, Facade, Proxy etc.

Behavioral Design Pattern

A behavioral design pattern abstracts an action you want to take from the object or class that takes the action. By changing the object or class, you can change the algorithm used, the objects affected, or the behavior, while still retaining the same basic interface for client classes.

e.g. – Chain of responsibility, Command, Mediator, Observer, Strategy, Template method etc.

Well …enough theory on the design pattern. I am planning next couple of posts on the sample Java implementation of various design pattern. I am not going to provide you any theory for specific design pattern however I’ll be providing you the reference of the resources where you can find theoretical description of a design pattern.

Sometimes, it is required to write a SQL script such that multiple executions on the script should not fail. To address such requirement, generally you have following options –

1. You can execute the SQL statements conditionally (with some check)
e.g. –

DROP TABLE IF EXISTS <table name> CASCADE;

INSERT INTO <table> (<pk_column>, <column_2>) SELECT 'pk_column_value', 'column_2_value' WHERE NOT EXISTS (SELECT 'Y' FROM <table> WHERE <pk_column> = 'some value' );

2. You can use the options provided in the database itself
e.g. –

CREATE OR REPLACE FUNCTION <function name>

We had the similar requirement. We wanted to create a script which should upgrade our data model from version x to version x+1 and it should not fail even if the script is executed multiple time. We could not use any of the above option in case of adding a column to an existing table.

PostgreSQL provide an option of checking if the column already exists or not while dropping the column. However, it does not provide such straight forward way while adding a column to check if the column is already there in the table or not. Also, we did not want to drop the column first and then add the column because in that case we might lose the data (if there is some).

Solution for the problem is to query the tables where Postgres stores the schema metadata. For checking if a column exists or not in a particular table, you need to execute a SELECT query on the JOIN of two tables – PG_ATTRIBUTE and PG_CLASS, which stores the information about columns and tables respectively (Query is highlighed in the code given below).

CREATE OR REPLACE FUNCTION test.upgrade_schema()
RETURNS void AS
$BODY$
DECLARE var_column_exists VARCHAR(32);
BEGIN
	SELECT A.ATTNAME INTO VAR_COLUMN_EXISTS FROM PG_ATTRIBUTE A, PG_CLASS C
	WHERE A.ATTRELID = C.OID AND A.ATTNAME = 'column_to_add' AND C.RELNAME = 'table_to_alter';

	IF var_column_exists IS NULL THEN
		ALTER TABLE test.table_to_alter ADD COLUMN  column_to_add varchar(256) DEFAULT NULL;
	END IF;
END;
$BODY$
LANGUAGE plpgsql
;

References

http://www.postgresql.org/docs/8.0/static/plpgsql.html
http://www.postgresql.org/docs/8.1/static/catalog-pg-class.html
http://www.postgresql.org/docs/8.1/static/catalog-pg-attribute.html

Recently,  I encountered an issue in exporting a PDF format report using Jasper API in which long text was not coming properly in the report. I had to spent quite a some time  to resolve the issue. Solution is very simple and worked for me perfectly fine. So, I thought of sharing the same with you all.

Text fields have dynamic content, most of the time you can’t anticipate the exact amount of space to provide for them. If the space you reserve for your text fields is not sufficient, the text content is truncated so that it fits into the available area.

This scenario is not always acceptable, so you can let the reporting engine calculate the amount of space required to display the entire content of the text field at run-time, and automatically adjust the size of the report element. To do this, set “isStretchWithOverflow” to true for the particular text field elements you are interested in. By doing this, you’ll ensure that if the specified height for the text field is not sufficient, it will automatically be increased in order to be able to display the entire text content.

This might work fine in case of export format like RTF, HTML or XLS etc., however, in case of PDF export, it might happen that word wrap does not work properly or portions of text to disappear at the end of longer text paragraphs. In the Figure-1, you can see that the generated report (in PDF format) is looking distorted –

Figure-1

To resolve this issue, the following jasper report configuration property – net.sf.jasperreports.export.pdf.force.linebreak.policy need to set to true as follows in the JRXML file –

<property name="net.sf.jasperreports.export.pdf.force.linebreak.policy" value="true"/>

To set the property through iReport, follow the steps mentioned below –

1. Right Click on the report in the “Report Inspector” panel and select the “Properties” option in the opened context menu as shown in Figure-2

Figure-2

2. Report properties dialog box will appear. Again, select “Properties” and click on the small button next to it in right side. This will pop up the configuration properties dialog as shown in Figure-3

Figure-3

3. Click on Add button. This will pop up a dialog box where you can add a new property and its value as shown in Figure-4.

Figure-4

4. Click OK button to save the newly added property. You can see the newly added property in the report configuration property dialog as shown in Figure-5.

Figure-5

5. Click the OK button.

Regenerate the report with the updated report template. This will resolve the word wrap/ line break issue and now the generated report should look as shown in Figure-6 which earlier was looking as shown in Figure-1

Figure-6

References –

http://jasperreports.sourceforge.net/config.reference.html#net.sf.jasperreports.export.pdf.force.linebreak.policy

This is a sample WLST script to create a Weblogic Domain. Script also shows how to configure a Server (Admin Server in this example) and how to deploy an application?

On the successful execution of the script, you will get a Weblogic domain created with specified name at the specified location and with one application already deployed.

WLST Script

# ===== wlsDomainCreationAndConfiguration.py - Crisp Code =======

windows = true;

pathSeparator = '\\';
if not windows:
	pathSeparator = '/';

domainLocation = '';
jvmLocation = '';
domainTemplate = '';

def intialize():
	global domainLocation;
	global jvmLocation;
	global domainTemplate;

	print 'Starting the initialization process';
	# Update the path
	loadProperties("Path/To/PropFile/wlsDomainCreationAndConfiguration.properties");

	domainLocation = domainsDirectory + pathSeparator + domainName;
	print 'Domain Location: ' + domainLocation;

	if len(jvmLocation) == 0:
		jvmLocation = middleWareHome + pathSeparator + 'jrockit_160_24_D1.1.2-4';
	print 'JVM Location: ' + jvmLocation;

	# Using Domain Template - wls.jar
	domainTemplate = middleWareHome + pathSeparator + 'wlserver_10.3' + pathSeparator + 'common' + pathSeparator + 'templates' + pathSeparator + 'domains' + pathSeparator + 'wls.jar';
	print 'Using Domain Template: ' + domainTemplate;

	print 'Initialization completed';

def configureAdminServer():
	print 'Starting Admin Server Configuration...';

	print 'Setting listen address/port...';
	cd('/')
	cd('Server/AdminServer')
	cmo.setListenAddress(listenAddress)
	cmo.setListenPort(int(listenPort))

	print 'SSL Settings...';
	create('AdminServer','SSL')
	cd('SSL/AdminServer')
	set('Enabled', enableSSL)
	set('ListenPort', int(sslListenPort))

	print 'Setting the username/password...';
	cd('/');
	cd('Security/base_domain/User/weblogic');
	cmo.setName(adminUserName);
	cmo.setPassword(adminPassword);
	print 'Admin Server Configuration Completed.';

def setStartupOptions():
	print('Setting StartUp Options...');
	setOption('CreateStartMenu', 'false');
	setOption('ServerStartMode', domainMode);
	setOption('JavaHome', jvmLocation);
	setOption('OverwriteDomain', 'false');

def createCustomDomain():
	print 'Creating the domain...';
	readTemplate(domainTemplate);

	configureAdminServer();
	setStartupOptions();

	writeDomain(domainLocation);
	closeTemplate();
	print 'Domain Created';

def startAndConnnectToAdminServer():
	connUri = 't3://localhost:%s' % listenPort
	print 'Connection URI : ' + connUri;

	print 'Starting the Admin Server...';
	startServer('AdminServer', domainName , connUri, adminUserName, adminPassword, domainLocation,'true',60000,'false');
	print 'Started the Admin Server';

	print 'Connecting to the Admin Server';
	connect(adminUserName, adminPassword, 't3://localhost:%s' % listenPort);
	print 'Connected';

def shutdownAndExit():
	print 'Shutting down the Admin Server...';
	shutdown();
	print 'Exiting...';
	exit();

def deployApplication():
	print 'Starting in edit mode..';
	edit();
	startEdit();

	print 'Deploying the ear..';
	deploy('MyApp01', path=appToDeploy);
	print 'Deployment completed';

	save();
	activate();

# ================================================================
#			Main Code Execution
# ================================================================

intialize();
createCustomDomain();
startAndConnnectToAdminServer();
deployApplication();
shutdownAndExit();
# =========== End Of Script ===============

Code Description
Line #20: load properties from a property file. (Content of the property file is listed in the next section)
Line #39-54: Setting the listening address/port, doing SSL configuration, and setting the user specified user name and password.
Line #57: Function for setting the Weblogic domain start-up options.
Line #66: There are two ways of creating domain – using createDomain or using readTemplate (then writeDomain and closeTemplate) command. If you want to modify the default domain, chose the latter one and that’s what we are doing here.
Line #71: Write the domain configuration information to the specified directory.
Line #72: Close the current domain template.
Line #75: Function for starting and connecting to the AdminServer.
Line #93: Function for deploying an application. If you don’t want to deploy any application, you can remove the call to the function – deployApplication at line #112.

Property File

# WLS Props
middleWareHome=F:\\Oracle\\Middleware

# Domain Props
domainName=ScriptDomain
domainsDirectory=D:\\TestDomains
domainMode=dev
#domainMode=prod

# User Credential Props
adminUserName=weblogic
adminPassword=web10gic

# Admin Server Props
adminServerName=AdminServer
listenAddress=localhost
listenPort=8001
enableSSL=true
sslListenPort=8002

#JVM Props
jvmLocation=

# Custom Props
appName=DemoApp
appToDeploy=Path\\To\\AppToDeploy

Running a WLST Script
Open a command shell and set the WebLogic environment by running the command:
setWLSEnv.cmd (for Windows or ./setWLSEnv.sh for UNIX like systems).

The file setWLSEnv.cmd/setWLSEnv.sh is located in the directory ${WL_HOME}/server/bin. To run a WLST script we can use something like:

java weblogic.WLST path_to_script/wlsDomainCreationAndConfiguration.py

References –
http://docs.oracle.com/cd/E15051_01/wls/docs103/config_scripting/using_WLST.html
http://docs.oracle.com/cd/E13222_01/wls/docs92/config_scripting/domains.html
http://docs.oracle.com/cd/E11035_01/wls100/wlsmbeanref/core/index.html

This sample code will help you to understand how you can upload multiple files by using the RESTful Web Service. Here I have used Apache’s Common Fileupload (ACF) API for uploading multiple files.  ACF parses HTTP requests which conform to RFC 1867, “Form-based File Upload in HTML”. That is, if an HTTP request is submitted using the POST method, and with a content type of “multipart/form-data”, then ACF can parse that request, and make the results available in a manner easily used by the caller.

Server Side Code –

package com.crispCode.rest;

import java.io.File;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@Path("/upload")
public class RESTMultipleFileUpload
{
    private static final String FILE_UPLOAD_PATH = "C:/WINDOWS/Temp";
    private static final String CANDIDATE_NAME = "candidateName";
    private static final String SUCCESS_RESPONSE = "Successful";
    private static final String FAILED_RESPONSE = "Failed";
    
    @POST
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces("text/plain")
    @Path("/multipleFiles")
    public String registerWebService(@Context HttpServletRequest request)
    {
        String responseStatus = SUCCESS_RESPONSE;        
        String candidateName = null;
        
        //checks whether there is a file upload request or not
        if (ServletFileUpload.isMultipartContent(request))
        {
            final FileItemFactory factory = new DiskFileItemFactory();
            final ServletFileUpload fileUpload = new ServletFileUpload(factory);
            try
            {
                /*
                 * parseRequest returns a list of FileItem
                 * but in old (pre-java5) style
                 */
                final List items = fileUpload.parseRequest(request);
                
                if (items != null)
                {
                    final Iterator iter = items.iterator();
                    while (iter.hasNext())
                    {
                        final FileItem item = (FileItem) iter.next();
                        final String itemName = item.getName();
                        final String fieldName = item.getFieldName(); 
                        final String fieldValue = item.getString();

                        if (item.isFormField())
                        {
                            candidateName = fieldValue;
                            System.out.println("Field Name: " + fieldName + ", Field Value: " + fieldValue);
                            System.out.println("Candidate Name: " + candidateName);
                        }
                        else
                        {
                                final File savedFile = new File(FILE_UPLOAD_PATH + File.separator
                                         + itemName);
                                System.out.println("Saving the file: " + savedFile.getName());
                                item.write(savedFile);                                
                        }

                    }
                }
            }
            catch (FileUploadException fue)
            {
                responseStatus = FAILED_RESPONSE;
                fue.printStackTrace();                
            }
            catch (Exception e)
            {
                responseStatus = FAILED_RESPONSE;
                e.printStackTrace();
            }
        }        
        
        System.out.println("Returned Response Status: " + responseStatus);
        
        return responseStatus;
    }
}

Code Description

Line #39: call the isMultipartContent() method of class ServletFileUpload which takes one parameter request. This method checks whether there is a file upload request or not. It returns a boolean value.

Line #41: If there is a request for uploading a file then create an object of DiskFileItemFactory class which implements FileItemFactory. The implementation of FileItemFactory means that it creates FileItem instances which keep their content either in memory or disk. While using the DiskFileItemFactory the temporary files are automatically deleted as soon as they are no longer needed. The interface FileItemFactoryis a factory for creating FileItem instances.

Line #42: Passing the object of FileItemFactory which has the reference of class DiskFileItemFactory inside the constructor of class ServletFileUpload(). This is used to constructs an instance of this class which uses the supplied factory to create FileItem instances.

Line #49: Calling the method parseRequest(), passing the request object which returns a list of FileItem instances parsed from the request, in the order they were transmitted.

Line #57: When the item if a file, item.getName() will return the file name otherwise null will be returned.

Line #61: FileItem, this interface represents a file or form item received within a multipart/form-data POST request. Checking if the item is Form field or File.

Line #69-72: if the item is file, save the file to disc using FileItem’s write() method, passing the instance of File object.

Line #92: Returning the response status. In case of any exception, ‘Failure’ response will be returned otherwise ‘Successful’ response will be returned.

Client Side (JSP) Code to Upload Multiple Files

<html>
<head>
<title>Multiple File Upload Demo Application</title>
</head>
<body>
	<form action="rest/upload/multipleFiles" method="POST"
		enctype="multipart/form-data">
		<div id="fileSection">
			<h1>Multiple File Upload Demo Application</h1>
			<table>
				<tr>
					<td>Candidate Name:</td>
					<td><input type="text" name="candidateName" size="45" />
					</td>
				</tr>
				<tr>
					<td>Candidate Info File:</td>
					<td><input type="file" name="infoFile" size="45" />
					</td>
				</tr>
				<tr>
					<td>Candidate's Photo:</td>
					<td><input type="file" name="imgFile" size="45" />
					</td>
				</tr>
			</table>
		</div>
		<p />
		<input type="submit" value="Upload" />
	</form>
</body>
</html>

Deployment descriptor File –

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>RESTMultipleFileUpload</display-name>
	<servlet>
		<servlet-name>jersey-serlvet</servlet-name>
		<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
		<init-param>
			<param-name>com.sun.jersey.config.property.packages</param-name>
			<param-value>com.crispCode.rest</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>jersey-serlvet</servlet-name>
		<url-pattern>/rest/*</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
</web-app>

Description of the deployment descriptor for registering the Jersey as the servlet dispatcher, refer my earlier post.

Required Jar Files

Following is the list of jar files required for successful development and deployment –

  • asm-3.3.1.jar
  • commons-codec-1.3.jar
  • commons-fileupload-1.2.1.jar
  • commons-io-1.3.2.jar
  • commons-logging-1.1.1.jar
  • jersey-bundle-1.12.jar
  • jersey-core-1.12.jar

References

http://commons.apache.org/
http://jersey.java.net/nonav/documentation/latest/index.html