edina.eframework.gefcdemo.controllers.ProcessErosionController.java Source code

Java tutorial

Introduction

Here is the source code for edina.eframework.gefcdemo.controllers.ProcessErosionController.java

Source

/*
 * Copyright 2011 by EDINA, University of Edinburgh
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package edina.eframework.gefcdemo.controllers;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.SimpleHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.stereotype.Controller;
import org.springframework.ui.velocity.VelocityEngineUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import edina.eframework.gefcdemo.domain.SoilErosionWps;
import edina.eframework.gefcdemo.domain.WpsResponse;
import edina.eframework.gefcdemo.generated.wps.ExecuteResponse;
import edina.eframework.gefcdemo.portrayal.MapserverGenerator;

/**
 * This controller drives the processing of the soil erosion algorithm. This
 * generates the WPS request, sends the request and processes the response. The
 * controller is called by two AJAX requests, one to process the landcover
 * preview, and the other to go ahead and process the full soil erosion model.
 * 
 * @author Andrew Seales
 */
@Controller
@RequestMapping(value = "/processmodel")
public class ProcessErosionController {

    private static final Log log = LogFactory.getLog(ProcessErosionController.class);

    // Velocity object generated by Spring
    private VelocityEngine velocityEngine;

    public void setVelocityEngine(VelocityEngine velocityEngine) {
        this.velocityEngine = velocityEngine;
    }

    // Classpath location of the WPS request template for processing the model
    private String templateLocation;

    public void setTemplateLocation(String templateLocation) {
        this.templateLocation = templateLocation;
    }

    // Classpath location of the WPS request template for previewing landcover
    private String growTemplateLocation;

    public void setGrowTemplateLocation(String growTemplateLocation) {
        this.growTemplateLocation = growTemplateLocation;
    }

    // URL to the WPS server minus parameters. A POST request is made to this.
    private URL wpsServer;

    public void setWpsServer(URL wpsServer) {
        this.wpsServer = wpsServer;
    }

    // Base output folder where the data from the WPS is saved
    private String wpsOutputDir;

    public void setWpsOutputDir(String wpsOutputDir) {
        this.wpsOutputDir = wpsOutputDir;
    }

    // Object which generates Mapserver WMS mapfiles
    private MapserverGenerator mapserverGenerator;

    public void setMapserverGenerator(MapserverGenerator mapserverGenerator) {
        this.mapserverGenerator = mapserverGenerator;
    }

    /**
     * Testing method for entering a mock model.
     */
    @RequestMapping(method = RequestMethod.GET)
    public String handleProcess(SoilErosionWps params) {
        return "wps";
    }

    /**
     * Method requested by the landcover preview button.
     * 
     * @param params parameters used when calculating the soil erosion model
     * @return the main gefcdemo view, i.e., updates the controls panel
     * @throws IOException
     * @throws JAXBException
     */
    @RequestMapping(method = RequestMethod.POST, params = "ajaxSource=previewGrow")
    public String handlePreviewGrow(SoilErosionWps params) throws IOException, JAXBException {

        generateWpsRequest(params, new WpsResponse(), growTemplateLocation,
                wpsOutputDir + "/aseales/landcoverPreview.tiff");
        mapserverGenerator.generateLandcoverConfiguration("aseales");

        return "gefcdemo";
    }

    /**
     * Method requested by the process soil erosion button.
     * 
     * @param params parameters used when calculating the soil erosion model
     * @param wpsResponse results from WPS process written to this object
     * @return the main gefcdemo view, i.e., updates the control panel
     * @throws IOException
     * @throws JAXBException
     */
    @RequestMapping(method = RequestMethod.POST, params = "ajaxSource=submitProcess")
    public String handleProcessResult(SoilErosionWps params, @ModelAttribute("wpsResponse") WpsResponse wpsResponse)
            throws IOException, JAXBException {

        generateWpsRequest(params, wpsResponse, templateLocation, wpsOutputDir + "/aseales/result.tiff");
        mapserverGenerator.generateResultConfiguration("aseales");

        return "gefcdemo";
    }

    /**
     * Brokers the WPS request and response. Supports two different WPS requests,
     * the landcover preview and the process soil erosion model requests.
     * 
     * @param params parameters used when calculating the soil erosion model
     * @param wpsResponse results from the WPS process are written to this object
     * @param template the VM template to use to generate the WPS request
     * @param wpsOutputFile the filename to write the TIFF result file
     * @throws IOException
     * @throws JAXBException
     */
    private void generateWpsRequest(SoilErosionWps params, WpsResponse wpsResponse, String template,
            String wpsOutputFile) throws IOException, JAXBException {

        Writer wpsRequest = new StringWriter();
        Map<String, Object> velocityMap = new HashMap<String, Object>();

        velocityMap.put("params", params);

        VelocityEngineUtils.mergeTemplate(velocityEngine, template, velocityMap, wpsRequest);
        wpsRequest.close();

        log.debug(wpsRequest.toString());
        log.debug("Submitting to " + wpsServer);

        RequestEntity entity = null;
        try {
            entity = new StringRequestEntity(wpsRequest.toString(), "text/xml", "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new MalformedURLException("Apparantly 'UTF-8' is an unsupported encoding type.");
        }

        PostMethod post = new PostMethod(wpsServer.toString());
        post.setRequestEntity(entity);

        HttpClient client = new HttpClient(new SimpleHttpConnectionManager());

        client.getHttpConnectionManager().getParams().setSoTimeout(0);
        client.getHttpConnectionManager().getParams().setConnectionTimeout(0);

        client.executeMethod(post);

        JAXBContext jaxbContext = JAXBContext.newInstance("edina.eframework.gefcdemo.generated.wps");
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

        ExecuteResponse executeResponse = null;
        // First save post data into a String so we can log any potential errors.
        // Response isn't very large from the WPS so this shouldn't be a problem.
        // Trying to access post data if a direct InputStream is passed to unmarshal
        // doesn't work.
        String postData = post.getResponseBodyAsString();
        try {
            executeResponse = (ExecuteResponse) unmarshaller
                    .unmarshal(new ByteArrayInputStream(postData.getBytes()));
        } catch (ClassCastException e) {
            log.error("Error from WPS:\n" + postData);
            throw e;
        }

        //executeResponse.getStatus().getProcessAccepted(); // TODO check for failed
        String resultUrl = executeResponse.getProcessOutputs().getOutput().get(0).getReference().getHref();

        log.debug("Output " + resultUrl);
        wpsResponse.setOutputUrl(new URL(resultUrl));
        wpsResponse.setOutputId(resultUrl.substring(resultUrl.lastIndexOf("id=") + 3, resultUrl.length()));
        wpsResponse.setStatus(200);

        // Save the WPS output data to a file mapserver can use
        File resultOutputFile = new File(wpsOutputFile);
        resultOutputFile.getParentFile().mkdirs();
        FileOutputStream resultFileStream = new FileOutputStream(resultOutputFile);
        InputStream resultInputFile = null;
        try {
            resultInputFile = wpsResponse.getOutputUrl().openStream();
            byte[] buffer = new byte[4096];
            int i;
            while ((i = resultInputFile.read(buffer)) != -1) {
                resultFileStream.write(buffer, 0, i);
            }
            resultFileStream.flush();
        } finally {
            try {
                resultInputFile.close();
            } catch (Exception e) {
            }
            try {
                resultFileStream.close();
            } catch (Exception e) {
            }
        }

        log.debug("Result saved to " + resultOutputFile);
    }
}