Virgo Homepage

Acknowledgements

This guide is inspired by "Getting Started - Uploading Files" from the Spring Guides.

Multi-part file uploads

The original guide uses Spring Boot to bootstrap the demo application. This guide walks through the necessary additional steps/configuration to get "Uploading Files" up and running on Virgo.

Shopping list

Note
Only Virgo Server for Apache Tomcat and Virgo Jetty Server are supported by the Virgo Tooling.

Ingredients

Preparations

Install the Virgo server let’s say into VIRGO_HOME. Start your Spring Tool Suite™ and add the Virgo Tooling to your IDE.

Note
For the impatient: This is the update site URL: http://download.eclipse.org/virgo/release/tooling.

"Define a new server" within the IDE and choose the directory of the previously chosen Virgo installation VIRGO_HOME.

Clone the code from Sample Applications with Git.

git clone git://git.eclipse.org/gitroot/virgo/org.eclipse.virgo.samples.git

Afterwards "Import (Existing Maven Projects)" living inside the folder guide-uploading-files into your IDE.

Note
Before you can drag’n’drop the project onto your freshly created Virgo Server you have to Virgo → Add OSGi Project Bundle Nature (via a right click on the project) within your IDE.

Directions

Virgo has no support for Springs @EnableAutoConfiguration. Thus you have to be explicit where Spring Boot configures the embedded Tomcat with reasonable defaults on your behalf. Last but not least Virgo is an OSGi runtime and every bundle needs to be explicit about its needs in the META-INF/MANIFEST.MF.

This is mostly about specifying necessary Import-Package and the Web-ContextPath the file upload application wants to be published.

Import-Bundle: org.springframework.beans,
 org.springframework.context,
 org.springframework.web,
 org.springframework.web.servlet
Import-Package: org.eclipse.virgo.web.dm,
 org.springframework.core.io;version="[3.1,4)",
 org.springframework.stereotype;version="[3.1,4)",
 org.springframework.util;version="[3.1,4)",
 org.springframework.web.bind.annotation;version="[3.1,4)",
 org.springframework.web.client;version="[3.1,4)",
 org.springframework.web.multipart;version="[3.1,4)"
Web-ContextPath: /upload-guide[]

You can mix and match the Virgo specific header Import-Bundle and OSGi standard header Import-Package within the MANIFEST.MF. In the sample the fine-tuning of the MANIFEST.MF is done within the Maven pom.xml.

Note
If you intend to run your bundles in different OSGi containers you have to limit yourself to the OSGi standard headers.

Next step is to configuring the servlet container via WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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" version="2.4">

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/root-context.xml
        </param-value>
    </context-param>

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.eclipse.virgo.web.dm.ServerOsgiBundleXmlWebApplicationContext</param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                /WEB-INF/spring/appServlet/servlet-context.xml
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>
Note
Most important the contextClass which is not the default from the Spring Framework, but org.eclipse.virgo.web.dm.ServerOsgiBundleXmlWebApplicationContext.
<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.eclipse.virgo.web.dm.ServerOsgiBundleXmlWebApplicationContext</param-value>
</context-param>[]

Finally the Spring web application context. Nothing Virgo specific here:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		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/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<context:component-scan base-package="org.eclipse.virgo.guides.uploading" />

	<beans:bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
		<beans:property name="messageConverters">
			<util:list>
				<beans:bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
			</util:list>
		</beans:property>
	</beans:bean>

	<!-- Configure the multipart resolver -->
	<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- one of the properties available; the maximum file size in bytes -->
		<beans:property name="maxUploadSize" value="50000000" />
	</beans:bean>

</beans:beans>

Let’s taste

For easy client-side consumption of the file upload you can use the FileUploader.

package org.eclipse.virgo.guides.uploading.internal;

import java.io.FileNotFoundException;

import org.springframework.core.io.FileSystemResource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

public class FileUploader {

    public static void main(String[] args) throws FileNotFoundException {
        if (args.length == 0) {
            System.out.println("Usage: Requires the name of a file to upload.");
            System.exit(1);
        }

        RestTemplate template = new RestTemplate();
        MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
        parts.add("name", args[0]);
        parts.add("file", new FileSystemResource(args[0]));
        String response = template.postForObject("http://localhost:8080/upload-guide/upload",
        			parts, String.class);
        System.out.println(response);
    }
}
Note
Please see the original guide "Getting Started - Uploading Files" for more background information about this client.

There is an easy way to use the FileUploader with Maven:

$ mvn package exec:java

Or if you are more used to curl and the command line simply run the following command within the project’s directory.

$ curl -F "name=sample.txt" -F "file=@sample.txt" http://localhost:8080/upload-guide/upload

Either way you should see the following output independent of your choice:

You successfully uploaded sample.txt into sample.txt-uploaded !