Skip to content

Proposal #1: ServiceBox action servlet with Spring Framework

Alejandro Díaz Torres edited this page Jan 30, 2014 · 2 revisions

Overview

Include Spring Framework on the project.

Proposed By

Alejandro Díaz (alediator)

Assigned to Release

ServiceBox 1.4, MapStore 1.5

State

Choose one of: Under Discussion, In Progress, Completed, Rejected, Deferred

Motivation

Improve this application to fix mapstore #138 issue

Proposal

Include the Spring Framework dependencies and generalize Servlets in a specific ServiceBox action servlet to allow to envelop requests in order to filter with specific callbacks configurable with a property overrider and the servicebox.properties.

The first callback is a file uploader one to fix mapstore #138 and is going to change the Servlets:

  • FileUploader
  • KMZUploader
  • FileDownloader
  • HTTPWebGISFileDownload
  • HTTPWebGISFileUpload
  • HTTPWebGISSave
  • UploadCanvas

Class/Interface Name

The ServiceBoxActionServlet reads the Spring context on the Servlet initialization and save the ActionHandler:

	/**
	 * Init method of the servlet. Don't forgot override it on the actions and
	 * change the action name
	 */
	public void init(ServletConfig servletConfig) throws ServletException {
		super.init(servletConfig);
		String appPropertyFile = getServletContext().getInitParameter(
				PROPERTY_FILE_PARAM);
		InputStream inputStream = ServiceBoxActionServlet.class
				.getResourceAsStream(appPropertyFile);
		try {
			properties.load(inputStream);
			ServletContext context = getServletContext();
			WebApplicationContext wac = WebApplicationContextUtils
					.getRequiredWebApplicationContext(context);
			String actionHandler = DEFAULT_ACTION_HANDLER;
			if (actionName != null
					&& properties != null
					&& properties.containsKey(actionName + "."
							+ DEFAULT_ACTION_HANDLER)) {
				actionHandler = properties.getProperty(actionName + "."
						+ DEFAULT_ACTION_HANDLER);
			}
			serviceBoxActionHandler = (ServiceBoxActionHandler) wac
					.getBean(actionHandler);
		} catch (IOException e) {
			if (LOGGER.isLoggable(Level.SEVERE)) {
				LOGGER.log(Level.SEVERE,
						"Error encountered while processing properties file", e);
			}
		} finally {
			try {
				if (inputStream != null)
					inputStream.close();
			} catch (IOException e) {
				if (LOGGER.isLoggable(Level.SEVERE))
					LOGGER.log(Level.SEVERE,
							"Error building the action configuration ", e);
				throw new ServletException(e.getMessage());
			}
		}
	}

The ServiceBoxActionHandler execute each callback configured on the GET and POST actions and call and return the last ServiceBoxActionParameters (from the last callback)

	/**
	 * Handle a GET request
	 * 
	 * @param request
	 * @param response
	 * 
	 * @return ServiceBoxActionParameters
	 * 
	 * @throws IOException
	 */
	public ServiceBoxActionParameters doGet(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		ServiceBoxActionParameters callbackResult = null;
		if (callbacks != null) {
			for (Callback callback : callbacks) {
				callbackResult = callback.onGet(request, response,
						callbackResult);
				if (!callbackResult.isSuccess()) {
					// not success: return false
					break;
				}
			}
		}
		return callbackResult;
	}

this callback includes the status of the callback execution and some parameters used inside each callback to allow it read request only one time and write rules in different callbacks.

public class ServiceBoxActionParameters {

	private boolean success = false;
	private List<FileItem> items;
	private Map<String, Object> extensions;

To use it, change a Servlet and extend ServiceBoxActionServlet

/**
 * Servlet implementation class FileUploader
 */
public class FileUploader extends ServiceBoxActionServlet {

and change the doGet and doPost methods:

	protected void doPostAction(HttpServletRequest request,
			HttpServletResponse response, ServiceBoxActionParameters actionParameters) throws ServletException, IOException {

		// get parameter name
		String moveFile = request.getParameter("moveFile");
		String type = request.getParameter("type");
		String fileToMoveName = request.getParameter("zipName");
		List<FileItem> items = null;

		File fileToMove = null;

		try {

			// create a file with a random name
			String uuid = UUID.randomUUID().toString();
			
			// File items are read only one time. Check if already exists on the actionParameters 
			if(actionParameters != null 
					&& actionParameters.isSuccess()
					&& actionParameters.getItems() != null){
				items = actionParameters.getItems();
				
			// see http://commons.apache.org/fileupload/using.html
			}else if (ServletFileUpload.isMultipartContent(request)) {
				// Create a factory for disk-based file items
				FileItemFactory factory = new DiskFileItemFactory();
				// Create a new file upload handler
				ServletFileUpload upload = new ServletFileUpload(factory);
				// Parse the request
				items = upload.parseRequest(request);
			}

			// Process the uploaded items
			if (items != null) {

the default callback can be configured on the property overrider:

# File upload callback configuration
fileUploadCallback.callbackConfiguration.maxSize=1000024
fileUploadCallback.callbackConfiguration.maxItems=100
fileUploadCallback.callbackConfiguration.tempFolder=/tmp
fileUploadCallback.callbackConfiguration.buffSize=1024

Then, if you send more than 100 items, the server return a message like this: { "success":false, "errorMessage":"Max items size exceeded (expected: '100', found: '200')."}

or if one of your uploaded files exceed the size limit the message is: { "success":false, "errorMessage":" Max item size exceeded (expected: '100024', found: '141357' on item 'test.kml')."}

Feedback

This section should contain feedback provided by members who may have a problem with the proposal.

Backwards Compatibility

This change needs to improve a litle bit the widgets on MapStore to read the error messages instead, but is compatible (it only knows that there are an error on the file upload, but it doesn't read the error message).

Voting

Alejandro Díaz: +1