package au.org.aurin.wif.controller.allocation; import static au.org.aurin.wif.io.RestAPIConstants.HEADER_USER_ID_KEY; import java.io.IOException; import java.net.MalformedURLException; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import org.geotools.filter.text.cql2.CQLException; 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.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 au.org.aurin.wif.controller.OWIURLs; import au.org.aurin.wif.exception.config.GeoServerConfigException; 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.DataStoreUnavailableException; import au.org.aurin.wif.exception.validate.AllocationAnalysisFailedException; import au.org.aurin.wif.exception.validate.IncompleteAllocationScenarioException; import au.org.aurin.wif.exception.validate.IncompleteDemandScenarioException; import au.org.aurin.wif.exception.validate.SuitabilityAnalysisFailedException; import au.org.aurin.wif.exception.validate.WifInvalidInputException; import au.org.aurin.wif.executors.svc.AsyncAllocationService; import au.org.aurin.wif.impl.suitability.WMSOutcome; import au.org.aurin.wif.model.allocation.AllocationScenario; import au.org.aurin.wif.model.reports.allocation.AllocationAnalysisReport; import au.org.aurin.wif.svc.WifKeys; import au.org.aurin.wif.svc.allocation.AllocationScenarioService; import au.org.aurin.wif.svc.report.ReportService; import com.vividsolutions.jts.io.ParseException; /** * The Class AllocationScenarioController. */ @Controller @RequestMapping(OWIURLs.PROJECT_SVC_URI) public class AllocationScenarioController { /** The Constant LOGGER. */ private static final Logger LOGGER = LoggerFactory .getLogger(AllocationScenarioController.class); /** The allocation scenario service. */ @Resource private AllocationScenarioService allocationScenarioService; /** The async allocation service. */ @Resource private AsyncAllocationService asyncAllocationService; /** The scenarios pool. */ // private final HashMap<String, Future<Boolean>> scenariosPool = new // HashMap<String, Future<Boolean>>(); private final HashMap<String, Future<String>> scenariosPool = new HashMap<String, Future<String>>(); /** The report service. */ @Autowired private ReportService reportService; /** * Sets the allocation scenario service. * * @param allocationScenarioService * the new allocation scenario service */ public void setAllocationScenarioService( final AllocationScenarioService allocationScenarioService) { this.allocationScenarioService = allocationScenarioService; } // TODO wrap the service calls in try-catch blocks so that we can // log what sort of exceptions were thrown while accessing the service. /** * Gets the allocation scenarios for project. * * @param roleId * the role id * @param projectId * the project id * @return the allocation scenarios for project * @throws WifInvalidInputException * the wif invalid input exception */ @RequestMapping(method = RequestMethod.GET, value = "/{projectId}/allocationScenarios", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody List<AllocationScenario> getAllocationScenariosForProject( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId) throws WifInvalidInputException { LOGGER .info( "*******>> getAllocationScenariosForProject request for project id ={}", projectId); return allocationScenarioService.getAllocationScenarios(projectId); } /** * Gets the allocation scenario. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @return the allocation 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}/allocationScenarios/{id}", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody AllocationScenario getAllocationScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws WifInvalidInputException, WifInvalidConfigException, ParsingException { LOGGER.info("*******>> getAllocationScenario request for project id ={}", projectId); try { return allocationScenarioService.getAllocationScenario(id); } catch (final WifInvalidInputException e) { LOGGER.error("getAllocationScenario failed: {}", e.getMessage()); throw new WifInvalidInputException("getAllocationScenario failed", e); } catch (final WifInvalidConfigException e) { LOGGER.error("getAllocationScenario failed: {}", e.getMessage()); throw new WifInvalidConfigException("getAllocationScenario failed", e); } catch (final ParsingException e) { LOGGER.error("getAllocationScenario failed: {}", e.getMessage()); throw new ParsingException("getAllocationScenario failed", e); } } /** * Creates the allocation scenario. * * @param roleId * the role id * @param projectId * the project id * @param allocationScenario * the allocation scenario * @param response * the response * @return the allocation scenario * @throws WifInvalidInputException * the wif invalid input exception * @throws WifInvalidConfigException * the wif invalid config exception * @throws ParsingException * the parsing exception * @throws IncompleteAllocationScenarioException * the incomplete allocation scenario exception * @throws FactoryException * @throws GeoServerConfigException * @throws DataStoreUnavailableException * @throws NoSuchAuthorityCodeException * @throws MalformedURLException * @throws IllegalArgumentException */ @RequestMapping(method = RequestMethod.POST, value = "/{projectId}/allocationScenarios", consumes = "application/json", produces = "application/json") @ResponseStatus(HttpStatus.CREATED) public @ResponseBody AllocationScenario createAllocationScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @RequestBody final AllocationScenario allocationScenario, final HttpServletResponse response) throws WifInvalidInputException, WifInvalidConfigException, ParsingException, IncompleteAllocationScenarioException, IllegalArgumentException, MalformedURLException, NoSuchAuthorityCodeException, DataStoreUnavailableException, GeoServerConfigException, FactoryException { LOGGER.info( "*******>> createAllocationScenario request for project id ={}", projectId); try { return allocationScenarioService.createAllocationScenario( allocationScenario, projectId); } catch (final WifInvalidInputException e) { LOGGER.error("createAllocationScenario failed: {}", e.getMessage()); throw new WifInvalidInputException("createAllocationScenario failed", e); } catch (final WifInvalidConfigException e) { LOGGER.error("createAllocationScenario failed: {}", e.getMessage()); throw new WifInvalidConfigException("createAllocationScenario failed", e); } catch (final ParsingException e) { LOGGER.error("createAllocationScenario failed: {}", e.getMessage()); throw new ParsingException("createAllocationScenario failed", e); } catch (final IncompleteAllocationScenarioException e) { LOGGER.error("createAllocationScenario failed: {}", e.getMessage()); throw new IncompleteAllocationScenarioException( "createAllocationScenario failed", e); } } /** * Update allocation scenario. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @param allocationScenario * the allocation scenario * @throws Exception * the exception */ @RequestMapping(method = RequestMethod.PUT, value = "/{projectId}/allocationScenarios/{id}", consumes = "application/json") @ResponseStatus(HttpStatus.NO_CONTENT) public void updateAllocationScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id, @RequestBody final AllocationScenario allocationScenario) throws Exception { LOGGER.info( "*******>> updateAllocationScenario request for project id ={}", projectId); try { allocationScenario.setId(id); allocationScenarioService.updateAllocationScenario(allocationScenario, projectId); } catch (final Exception e) { LOGGER.error("updateAllocationScenario failed: {}", e.getMessage()); throw new Exception("updateAllocationScenario failed", e); } } /** * Delete allocation scenario. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @throws Exception * the exception */ @RequestMapping(method = RequestMethod.DELETE, value = "/{projectId}/allocationScenarios/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteAllocationScenario( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws Exception { LOGGER.info( "*******>> deleteAllocationScenario request for project id ={}", projectId); try { allocationScenarioService.deleteAllocationScenario(id, projectId); } catch (final WifInvalidInputException e) { LOGGER.error("deleteAllocationScenario failed: {}", e.getMessage()); throw new WifInvalidInputException("deleteAllocationScenario failed", e); } catch (final WifInvalidConfigException e) { LOGGER.error("deleteAllocationScenario failed: {}", e.getMessage()); throw new WifInvalidConfigException("deleteAllocationScenario failed", e); } catch (final ParsingException e) { LOGGER.error("deleteAllocationScenario failed: {}", e.getMessage()); throw new ParsingException("deleteAllocationScenario failed", e); } } /** * Gets the outcome async. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @return the 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 * @throws IncompleteDemandScenarioException * @throws SQLException * @throws ClassNotFoundException */ @RequestMapping(method = RequestMethod.POST, value = "/{projectId}/allocationScenarios/{id}/asyncOutcome", produces = "application/json") @ResponseStatus(HttpStatus.OK) public void getOutcomeAsync( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws WifInvalidInputException, WifInvalidConfigException, MismatchedDimensionException, NoSuchAuthorityCodeException, FactoryException, TransformException, ParseException, IOException, SuitabilityAnalysisFailedException, CQLException, InterruptedException, ExecutionException, ParsingException, IncompleteDemandScenarioException, ClassNotFoundException, SQLException { LOGGER .info( "*******>> getOutcomeAsync request for allocation scenario id ={}", id); try { // final Future<Boolean> outcome = asyncAllocationService // .doAllocationAnalysisAsync(id); final Future<String> outcome = asyncAllocationService .doAllocationAnalysisAsync(id); 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 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 * @throws ParsingException * the parsing exception * @throws ExecutionException * @throws InterruptedException */ @RequestMapping(method = RequestMethod.GET, value = "/{projectId}/allocationScenarios/{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, ParsingException, InterruptedException, ExecutionException { // LOGGER.info( // "*******>> getScenarioStatus request for allocation scenario id ={}", // id); final HashMap<String, String> answer = new HashMap<String, String>(2); answer.put(WifKeys.SETUP_PROCESS_KEY, WifKeys.ALLOCATION_PROCESS_STATE_SETUP); String message = WifKeys.PROCESS_STATE_NA; final AllocationScenario allocationScenario = allocationScenarioService .getAllocationScenario(id); if (allocationScenario.isReady()) { message = WifKeys.PROCESS_STATE_SUCCESS; // ////// new ali final Future<String> result = scenariosPool.get(id); message = result.get(); if (message.equals("")) { message = WifKeys.PROCESS_STATE_SUCCESS; } // //////////// } else { try { // final Future<Boolean> result = scenariosPool.get(id); final Future<String> 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()) { // message = WifKeys.PROCESS_STATE_SUCCESS; // scenariosPool.remove(id); // // TODO Probably this is not necessary...only time will tell // // allocationScenario.setReady(true); // // allocationScenarioService.updateAllocationScenario( // // allocationScenario, projectId); // } else { // message = WifKeys.PROCESS_STATE_RUNNING; // } if (result.isDone()) { try { // final Boolean msg = result.get(); final String msg = result.get(); LOGGER.info("process ended with result: {}", msg); } catch (final ExecutionException e) { message = WifKeys.PROCESS_STATE_FAILED; final String errorMessage = "Allocation analysis asynchronous process failed"; answer.put(WifKeys.STATUS_KEY, message); LOGGER.info("Status is = {}", answer.get(WifKeys.STATUS_KEY)); LOGGER.error(errorMessage, e); scenariosPool.remove(id); throw new AllocationAnalysisFailedException(errorMessage, e); } message = WifKeys.PROCESS_STATE_SUCCESS; // ////// new ali message = result.get(); if (message.equals("")) { message = WifKeys.PROCESS_STATE_SUCCESS; } // /////////// scenariosPool.remove(id); } else { message = 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.toString(), e); } } } answer.put(WifKeys.STATUS_KEY, message); // LOGGER.info("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}/allocationScenarios/{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 allocation request for allocationScenario id ={}", id); try { return allocationScenarioService.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 allocation scenario report. * * @param roleId * the role id * @param projectId * the project id * @param id * the id * @return the allocation 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}/allocationScenarios/{id}/report", produces = "application/json") @ResponseStatus(HttpStatus.OK) public @ResponseBody AllocationAnalysisReport getAllocationScenarioReport( @RequestHeader(HEADER_USER_ID_KEY) final String roleId, @PathVariable("projectId") final String projectId, @PathVariable("id") final String id) throws WifInvalidInputException, WifInvalidConfigException, ParsingException { LOGGER.info( "*****>> getAllocationScenarioReport request for scenario id ={}", id); final AllocationScenario allocationScenario = allocationScenarioService .getAllocationScenario(id); return reportService.getAllocationAnalysisReport(allocationScenario); } }