/* (c) 2017 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.importer.rest; import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java.util.Map; import org.geoserver.catalog.StoreInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.importer.ImportContext; import org.geoserver.importer.ImportData; import org.geoserver.importer.ImportFilter; import org.geoserver.importer.Importer; import org.geoserver.importer.ValidationException; import org.geoserver.rest.RequestInfo; import org.geoserver.rest.RestBaseController; import org.geoserver.rest.RestException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; @RestController @RequestMapping(path = RestBaseController.ROOT_PATH+"/imports", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_HTML_VALUE }) public class ImportController extends ImportBaseController { @Autowired public ImportController(Importer importer) { super(importer); } @PostMapping(value = {"/{id}",""}) @ResponseStatus(HttpStatus.CREATED) public ResponseEntity<Object> postImports( @PathVariable(required=false) Long id, @RequestParam(name="async", required = false, defaultValue = "false") boolean async, @RequestParam(name="exec", required = false, defaultValue = "false") boolean exec, @RequestBody(required=false) ImportContext obj, UriComponentsBuilder builder) throws IOException { ImportContext context = (ImportContext) context(id, true, false); if (context != null) { try { runImport(context, async); } catch (Throwable t) { if (t instanceof ValidationException) { throw new RestException(t.getMessage(), HttpStatus.BAD_REQUEST, t); } else { throw new RestException("Error occured executing import", HttpStatus.INTERNAL_SERVER_ERROR, t); } } return new ResponseEntity<>("", new HttpHeaders(), HttpStatus.NO_CONTENT); } context = createImport(id, obj, async, exec); if (context != null) { importer.changed(context); } else { throw new RestException("Error occured executing import", HttpStatus.INTERNAL_SERVER_ERROR); } UriComponents uriComponents = getUriComponents(context.getId().toString(), builder); HttpHeaders headers = new HttpHeaders(); headers.setLocation(uriComponents.toUri()); return new ResponseEntity<>(context, headers, HttpStatus.CREATED); } @GetMapping public ImportWrapper getImports(@RequestParam(required=false) String expand) { Object lookupContext = context(null, true, true); if (lookupContext == null) { // this means a specific lookup failed throw new RestException("Failed to find import context", HttpStatus.NOT_FOUND); } else { return (writer, builder, converter) -> converter.contexts(builder,(Iterator<ImportContext>)lookupContext, converter.expand(expand, 0)); } } @GetMapping(value = "/{id}") public ImportContext getImports(@PathVariable Long id) { return context(id); } @PutMapping(value = "/{id}") @ResponseStatus(HttpStatus.CREATED) public ResponseEntity<Object> putImport( @PathVariable Long id, @RequestParam(name="async", required = false, defaultValue = "false") boolean async, @RequestParam(name="exec", required = false, defaultValue = "false") boolean exec, UriComponentsBuilder builder) { if (id != null) { ImportContext context = createImport(id, null, async, exec); assert context.getId() >= id; UriComponents uriComponents = getUriComponents(context.getId().toString(), builder); HttpHeaders headers = new HttpHeaders(); headers.setLocation(uriComponents.toUri()); return new ResponseEntity<>(context, headers, HttpStatus.CREATED); } else { throw new RestException("ID must be provided for PUT", HttpStatus.BAD_REQUEST); } } @DeleteMapping(value = {"", "/{id}"}) @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteImports(@PathVariable(required = false) Long id) { Iterator<ImportContext> contexts = null; if (id == null) { contexts = importer.getAllContexts(); } else { contexts = Collections.singletonList(context(id)).iterator(); } while (contexts.hasNext()) { ImportContext ctx = contexts.next(); if (ctx.getState() != ImportContext.State.COMPLETE) { try { importer.delete(ctx); } catch (IOException ioe) { throw new RestException("Error deleting context " + ctx.getId(), HttpStatus.INTERNAL_SERVER_ERROR, ioe); } } } } private UriComponents getUriComponents(String name, UriComponentsBuilder builder) { return builder.path("/imports/{id}").buildAndExpand(name); } private void runImport(ImportContext context, boolean async) throws IOException { if (context.getState() == ImportContext.State.INIT) { throw new RestException("Import context is still in INIT state, cannot run it yet", HttpStatus.PRECONDITION_FAILED); } // if the import is empty, prep it but leave data as is if (context.getTasks().isEmpty()) { importer.init(context, false); } Map<String, String[]> query = RequestInfo.get().getQueryMap(); if (async) { importer.runAsync(context, ImportFilter.ALL, false); } else { importer.run(context); } } private ImportContext createImport(Long id, ImportContext newContext, boolean async, boolean execute) { // create a new import ImportContext context; try { if (async) { context = importer.registerContext(id); } else { context = importer.createContext(id); } ImportData data = null; if (newContext != null) { WorkspaceInfo targetWorkspace = newContext.getTargetWorkspace(); StoreInfo targetStore = newContext.getTargetStore(); if (targetWorkspace != null) { // resolve to the 'real' workspace WorkspaceInfo ws = importer.getCatalog() .getWorkspaceByName(newContext.getTargetWorkspace().getName()); if (ws == null) { throw new RestException( "Target workspace does not exist : " + newContext.getTargetStore().getName(), HttpStatus.BAD_REQUEST); } context.setTargetWorkspace(ws); } if (targetStore != null) { StoreInfo ts = importer.getCatalog() .getStoreByName(newContext.getTargetStore().getName(), StoreInfo.class); if (ts == null) { throw new RestException( "Target store does not exist : " + newContext.getTargetStore().getName(), HttpStatus.BAD_REQUEST); } context.setTargetStore(ts); } if (targetStore != null && targetWorkspace == null) { // take it from the store context.setTargetWorkspace(targetStore.getWorkspace()); } context.setData(newContext.getData()); context.getDefaultTransforms().addAll(newContext.getDefaultTransforms()); } else if (context==null){ context = context(id, true); } if (!async && context.getData() != null) { importer.init(context, true); } context.reattach(importer.getCatalog(), true); importer.changed(context); if (async && context.getData() != null) { if (execute) { importer.runAsync(context, ImportFilter.ALL, true); } else { importer.initAsync(context, true); } } else if (execute && context.getData() != null) { importer.run(context); } } catch (IOException e) { throw new RestException("Unable to create import", HttpStatus.INTERNAL_SERVER_ERROR, e); } catch (IllegalArgumentException iae) { throw new RestException(iae.getMessage(), HttpStatus.BAD_REQUEST, iae); } return context; } }