package au.org.aurin.wif.controller.suitability; import static au.org.aurin.wif.io.RestAPIConstants.HEADER_USER_ID_KEY; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import org.geotools.feature.FeatureCollection; import org.geotools.filter.text.cql2.CQLException; import org.opengis.feature.Feature; import org.opengis.feature.type.FeatureType; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.operation.TransformException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.multipart.MultipartFile; import com.vividsolutions.jts.io.ParseException; import au.org.aurin.wif.controller.OWIURLs; import au.org.aurin.wif.exception.config.InvalidEntityIdException; import au.org.aurin.wif.exception.config.ParsingException; import au.org.aurin.wif.exception.config.WifInvalidConfigException; import au.org.aurin.wif.exception.io.DatabaseFailedException; import au.org.aurin.wif.exception.validate.SuitabilityAnalysisFailedException; import au.org.aurin.wif.exception.validate.WifInvalidInputException; import au.org.aurin.wif.executors.svc.AsyncSuitabilityService; import au.org.aurin.wif.impl.suitability.WMSOutcome; import au.org.aurin.wif.model.reports.suitability.SuitabilityAnalysisReport; import au.org.aurin.wif.model.suitability.SuitabilityScenario; import au.org.aurin.wif.svc.WifKeys; import au.org.aurin.wif.svc.report.ReportService; import au.org.aurin.wif.svc.suitability.SuitabilityScenarioService; /** * The Class SuitabilityScenarioController. */ @Controller @RequestMapping(OWIURLs.PROJECT_SVC_URI) public class SuitabilityScenarioController { /** The Constant LOGGER. */ private static final Logger LOGGER = LoggerFactory .getLogger(SuitabilityScenarioController.class); /** The suitability scenario service. */ @Resource private SuitabilityScenarioService suitabilityScenarioService; /** The async suitability service. */ @Resource private AsyncSuitabilityService asyncSuitabilityService; /** The scenarios pool. */ private final HashMap<String, Future<Boolean>> scenariosPool = new HashMap<String, Future<Boolean>>(); /** The report service. */ @Autowired private ReportService reportService; /** * Sets the suitability scenario service. * * @param suitabilityScenarioService * the new suitability scenario service */ public void setSuitabilityScenarioService( final SuitabilityScenarioService suitabilityScenarioService) { this.suitabilityScenarioService = suitabilityScenarioService; } /** * Gets the suitability scenarios for project. * * @param roleId * the role id * @param projectId * the project id * @return the suitability scenarios for project * @throws WifInvalidInputException * the wif invalid input exception */ @RequestMapping(method = RequestMethod.GET, value = "/{projectId}/suitabilityScenarios", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody List<SuitabilityScenario> getSuitabilityScenariosForProject( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId) throws WifInvalidInputException { LOGGER .info( "*******>> getSuitabilityScenariosForProject request for project id ={}", projectId); return suitabilityScenarioService.getSuitabilityScenarios(projectId); } /** * Gets the suitability scenario. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @return the suitability scenario * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception * @throws ParsingException * the parsing exception */ @RequestMapping(method = RequestMethod.GET, value = "/{projectId}/suitabilityScenarios/{id}", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody SuitabilityScenario getSuitabilityScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws WifInvalidInputException, WifInvalidConfigException, ParsingException { LOGGER.info("*******>> getSuitabilityScenario request for project id ={}", projectId); return suitabilityScenarioService.getSuitabilityScenario(id); } /** * Creates the suitability scenario. * * @param roleId * the role id * @param projectId * the project id * @param suitabilityScenario * the suitability scenario * @param response * the response * @return the suitability scenario * @throws WifInvalidInputException * the wif invalid input exception * @throws BindException * the bind exception * @throws WifInvalidConfigException * the wif invalid config exception * @throws ParsingException * the parsing exception */ @RequestMapping(method = RequestMethod.POST, value = "/{projectId}/suitabilityScenarios", consumes = "application/json", produces = "application/json") @ResponseStatus(HttpStatus.CREATED) public @ResponseBody SuitabilityScenario createSuitabilityScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @RequestBody final SuitabilityScenario suitabilityScenario, final HttpServletResponse response) throws WifInvalidInputException, BindException, WifInvalidConfigException, ParsingException { LOGGER.info( "*******>> createSuitabilityScenario request for project id ={}", projectId); return suitabilityScenarioService.createSuitabilityScenario( suitabilityScenario, projectId); } /** * Update suitability scenario. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @param suitabilityScenario * the suitability scenario * @throws WifInvalidInputException * the wif invalid input exception * @throws BindException * the bind exception * @throws WifInvalidConfigException * the wif invalid config exception */ @RequestMapping(method = RequestMethod.PUT, value = "/{projectId}/suitabilityScenarios/{id}", consumes = "application/json") @ResponseStatus(HttpStatus.NO_CONTENT) public void updateSuitabilityScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id, @RequestBody final SuitabilityScenario suitabilityScenario) throws WifInvalidInputException, BindException, WifInvalidConfigException { LOGGER.info( "*******>> updateSuitabilityScenario request for project id ={}", projectId); suitabilityScenarioService.updateSuitabilityScenario(suitabilityScenario, projectId); } /** * Delete suitability scenario. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception */ @RequestMapping(method = RequestMethod.DELETE, value = "/{projectId}/suitabilityScenarios/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteSuitabilityScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws WifInvalidInputException, WifInvalidConfigException { LOGGER.info( "*******>> deleteSuitabilityScenario request for project id ={}", projectId); suitabilityScenarioService.deleteSuitabilityScenario(id, projectId); } /** * Gets the wMS outcome. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @param getWmsOutcomeParams * the get wms outcome params * @return the wMS outcome * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception * @throws MismatchedDimensionException * the mismatched dimension exception * @throws NoSuchAuthorityCodeException * the no such authority code exception * @throws FactoryException * the factory exception * @throws TransformException * the transform exception * @throws ParseException * the parse exception * @throws IOException * Signals that an I/O exception has occurred. * @throws SuitabilityAnalysisFailedException * the wif analysis failed exception * @throws CQLException * the cQL exception * @throws ParsingException * the parsing exception */ @RequestMapping(method = RequestMethod.POST, value = "/{projectId}/suitabilityScenarios/{id}/wms", produces = "application/json") @ResponseStatus(HttpStatus.NO_CONTENT) public void getWMSOutcome( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id, @RequestBody final Map<String, String> getWmsOutcomeParams) throws WifInvalidInputException, WifInvalidConfigException, MismatchedDimensionException, NoSuchAuthorityCodeException, FactoryException, TransformException, ParseException, IOException, SuitabilityAnalysisFailedException, CQLException, ParsingException { LOGGER.info("*******>> getWMSOutcome request for project id ={}", projectId); try { final String areaAnalyzed = getWmsOutcomeParams.get("areaAnalyzed"); final String crsArea = getWmsOutcomeParams.get("crsArea"); suitabilityScenarioService.getWMSOutcome(id, areaAnalyzed, crsArea); } catch (final WifInvalidConfigException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new WifInvalidConfigException(e.getMessage(), e); } catch (final WifInvalidInputException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new WifInvalidInputException(e.getMessage(), e); } catch (final MismatchedDimensionException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new MismatchedDimensionException(e.getMessage(), e); } catch (final CQLException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new CQLException(e.getMessage()); } } /** * Gets the wMS outcome async. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @param getWmsOutcomeParams * the get wms outcome params * @return the wMS outcome async * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception * @throws MismatchedDimensionException * the mismatched dimension exception * @throws NoSuchAuthorityCodeException * the no such authority code exception * @throws FactoryException * the factory exception * @throws TransformException * the transform exception * @throws ParseException * the parse exception * @throws IOException * Signals that an I/O exception has occurred. * @throws SuitabilityAnalysisFailedException * the wif analysis failed exception * @throws CQLException * the cQL exception * @throws InterruptedException * the interrupted exception * @throws ExecutionException * the execution exception * @throws ParsingException * the parsing exception */ @RequestMapping(method = RequestMethod.POST, value = "/{projectId}/suitabilityScenarios/{id}/async/wms", produces = "application/json") @ResponseStatus(HttpStatus.OK) public void getWMSOutcomeAsync( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id, @RequestBody final Map<String, String> getWmsOutcomeParams) throws WifInvalidInputException, WifInvalidConfigException, MismatchedDimensionException, NoSuchAuthorityCodeException, FactoryException, TransformException, ParseException, IOException, SuitabilityAnalysisFailedException, CQLException, InterruptedException, ExecutionException, ParsingException { LOGGER.info("*******>> getWMSOutcomeAsync request for project id ={}", projectId); try { final String areaAnalyzed = getWmsOutcomeParams.get("areaAnalyzed"); final String crsArea = getWmsOutcomeParams.get("crsArea"); LOGGER.info("areaAnalyzed ={}", areaAnalyzed); LOGGER.info("crsArea ={}", crsArea); final Future<Boolean> outcome = asyncSuitabilityService .doSuitabilityAnalysisWMSAsync(id, areaAnalyzed, crsArea); scenariosPool.put(id, outcome); } catch (final WifInvalidConfigException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new WifInvalidConfigException(e.getMessage(), e); } catch (final WifInvalidInputException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new WifInvalidInputException(e.getMessage(), e); } catch (final MismatchedDimensionException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new MismatchedDimensionException(e.getMessage(), e); } } /** * Gets the outcome. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @param getOutcomeParams * the get outcome params * @return the outcome * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception * @throws MismatchedDimensionException * the mismatched dimension exception * @throws NoSuchAuthorityCodeException * the no such authority code exception * @throws FactoryException * the factory exception * @throws TransformException * the transform exception * @throws ParseException * the parse exception * @throws IOException * Signals that an I/O exception has occurred. * @throws SuitabilityAnalysisFailedException * the wif analysis failed exception * @throws CQLException * the cQL exception * @throws ParsingException * the parsing exception * @throws DatabaseFailedException */ @SuppressWarnings({ "rawtypes", "unchecked" }) @RequestMapping(method = RequestMethod.POST, value = "/{projectId}/suitabilityScenarios/{id}/outcome", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody FeatureCollection<FeatureType, Feature> getOutcome( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id, @RequestBody final Map<String, String> getOutcomeParams) throws WifInvalidInputException, WifInvalidConfigException, MismatchedDimensionException, NoSuchAuthorityCodeException, FactoryException, TransformException, ParseException, IOException, SuitabilityAnalysisFailedException, CQLException, ParsingException, DatabaseFailedException { LOGGER.info("*******>> getOutcome request for project id ={}", projectId); try { final String areaAnalyzed = getOutcomeParams.get("areaAnalyzed"); final String crsArea = getOutcomeParams.get("crsArea"); return (FeatureCollection) suitabilityScenarioService.getOutcome(id, areaAnalyzed, crsArea); } catch (final WifInvalidConfigException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new WifInvalidConfigException(e.getMessage(), e); } catch (final WifInvalidInputException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new WifInvalidInputException(e.getMessage(), e); } catch (final MismatchedDimensionException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new MismatchedDimensionException(e.getMessage(), e); } catch (final CQLException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new CQLException(e.getMessage()); } } /** * Gets the status. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @return the status * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception */ @RequestMapping(method = RequestMethod.GET, value = "/{projectId}/suitabilityScenarios/{id}/status", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody HashMap<String, String> getStatus( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws WifInvalidInputException, WifInvalidConfigException, SuitabilityAnalysisFailedException { LOGGER.debug("*******>> getScenarioStatus request for scenario id ={}", id); final HashMap<String, String> answer = new HashMap<String, String>(2); answer.put(WifKeys.SETUP_PROCESS_KEY, WifKeys.SUITABILITY_PROCESS_STATE_SETUP); String statusMessage = WifKeys.PROCESS_STATE_NA; final SuitabilityScenario suitabilityScenario = suitabilityScenarioService .getSuitabilityScenarioNoMapping(id); if (suitabilityScenario.getReady()) { statusMessage = WifKeys.PROCESS_STATE_SUCCESS; } else { try { final Future<Boolean> result = scenariosPool.get(id); if (result == null) { LOGGER.error("id not found in scenariosPool for {}", id); throw new WifInvalidInputException("id not found in scenariosPool"); } if (result.isDone()) { try { final Boolean msg = result.get(); LOGGER.info("process ended with result: {}", msg); } catch (final ExecutionException e) { statusMessage = WifKeys.PROCESS_STATE_FAILED; final String errorMessage = "suitability analysis asynchronous process failed"; answer.put(WifKeys.STATUS_KEY, statusMessage); LOGGER.info("Status is = {}", answer.get(WifKeys.STATUS_KEY)); LOGGER.error(errorMessage, e); scenariosPool.remove(id); throw new SuitabilityAnalysisFailedException(errorMessage, e); } statusMessage = WifKeys.PROCESS_STATE_SUCCESS; scenariosPool.remove(id); } else { statusMessage = WifKeys.PROCESS_STATE_RUNNING; } } catch (final Exception e) { if (e instanceof InterruptedException) { LOGGER.error("get status failed for {}", id); throw new InvalidEntityIdException("get status failed ", e); } } } answer.put(WifKeys.STATUS_KEY, statusMessage); LOGGER.debug("Status is ={}", answer.get(WifKeys.STATUS_KEY)); return answer; } /** * Gets the wms. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @return the wms * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception * @throws ParsingException * the parsing exception */ @RequestMapping(method = RequestMethod.GET, value = "/{projectId}/suitabilityScenarios/{id}/wmsinfo", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody WMSOutcome getWMS(@RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws WifInvalidInputException, WifInvalidConfigException, ParsingException { LOGGER.info("*******>> getWMS request for suitabilityScenario id ={}", id); try { return suitabilityScenarioService.getWMS(id); } catch (final WifInvalidConfigException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new WifInvalidConfigException(e.getMessage(), e); } catch (final WifInvalidInputException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new WifInvalidInputException(e.getMessage(), e); } catch (final ParsingException e) { LOGGER.error("getOutcome failed: {}", e.getMessage()); throw new ParsingException(e.getMessage(), e); } } /** * Gets the suitability scenario report based on the latest analysis * configuration. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @return the suitability scenario report * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception * @throws ParsingException * the parsing exception */ @RequestMapping(method = RequestMethod.GET, value = "/{projectId}/suitabilityScenarios/{id}/report", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody SuitabilityAnalysisReport getSuitabilityScenarioReport( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws WifInvalidInputException, WifInvalidConfigException, ParsingException { LOGGER.info( "*******>> getSuitabilityScenarioReport request for scenario id ={}", id); final SuitabilityScenario suitabilityScenario = suitabilityScenarioService .getSuitabilityScenario(id); return reportService.getSuitabilityAnalysisReport(suitabilityScenario); } /** * Gets the suitabilityLUsScores * * @param roleId * the role id * @param projectId * the project id * @return String * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException */ @RequestMapping(method = RequestMethod.GET, value = "/{projectId}/suitabilityLUsScores", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody List<String> getSuitabilityLUsScoresForProject( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId) throws WifInvalidInputException, WifInvalidConfigException { LOGGER .info( "*******>> getSuitabilityLUsScoresForProject request for project id ={}", projectId); return reportService.getSuitabilityLUsScores(projectId); } /** * duplicates suitability scenario. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @param inparams * the input name * @return List<String> * @throws Exception */ @RequestMapping(method = RequestMethod.POST, value = "/{projectId}/suitabilityScenarios/{id}/duplicate", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody List<String> duplicateScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectID, @PathVariable("id") final String scenarioID, @RequestBody final Map<String, String> params) { LOGGER.info("*******>> duplicate request for scenario id ={}, with a new name: {}", scenarioID, params.get("name")); final List<String> out= new ArrayList<String>(); try { final List<SuitabilityScenario> listScenario= suitabilityScenarioService.getSuitabilityScenarios(projectID); Boolean lsw= true; for (final SuitabilityScenario st: listScenario) { if (st.getLabel().equals(params.get("name"))) { lsw = false; } } if (lsw == true) { out.add(suitabilityScenarioService.duplicateSuitabiliyScenario(projectID, scenarioID, params.get("name"))); } else { out.add("Name already exists!"); } } catch (final Exception e) { LOGGER.error("duplicateScenario failed: {}", e.getMessage()); out.add("Error!" + e.getMessage()); } return out; } /** * duplicates suitability scenario. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @param inparams * the input name * @return List<String> * @throws Exception */ @RequestMapping(method = RequestMethod.POST, value = "/{projectId}/suitabilityScenarios/{id}/uploadXlsFactors", produces = "text/html") @ResponseStatus(HttpStatus.OK) public @ResponseBody String uploadFactorXLS( @PathVariable("projectId") final String projectID, @PathVariable("id") final String scenarioID, @RequestBody final MultipartFile file ) { final String filename= file.getOriginalFilename(); LOGGER.info("*******>> uploadXlsFactors request for scenario id ={}, with file name: {}", scenarioID, filename); String out=""; try { if (!file.isEmpty()) { out = suitabilityScenarioService.uploadXlsFactors(projectID, scenarioID, file.getInputStream()); } else { out = "{\"result\" : \"" +"File is not correct." +"\"}"; } } catch (final Exception e) { LOGGER.error("uploadXlsFactors failed: {}", e.getMessage()); out ="Error: " + e.getMessage(); //out="success"; out = "{\"result\" : \"" + out +"\"}"; } return out; } }