| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.rest.internal.web.controllers; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import io.swagger.annotations.ApiOperation; |
| import io.swagger.annotations.ApiResponse; |
| import io.swagger.annotations.ApiResponses; |
| import org.apache.logging.log4j.Logger; |
| import org.springframework.http.HttpHeaders; |
| import org.springframework.http.HttpStatus; |
| import org.springframework.http.MediaType; |
| import org.springframework.http.ResponseEntity; |
| import org.springframework.security.access.prepost.PreAuthorize; |
| import org.springframework.web.bind.annotation.PathVariable; |
| import org.springframework.web.bind.annotation.RequestMapping; |
| import org.springframework.web.bind.annotation.RequestMethod; |
| |
| import org.apache.geode.cache.LowMemoryException; |
| import org.apache.geode.cache.Region; |
| import org.apache.geode.cache.execute.Execution; |
| import org.apache.geode.cache.execute.FunctionException; |
| import org.apache.geode.cache.execute.FunctionService; |
| import org.apache.geode.cache.execute.ResultCollector; |
| import org.apache.geode.internal.cache.InternalRegion; |
| import org.apache.geode.internal.cache.execute.util.FindRestEnabledServersFunction; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| import org.apache.geode.rest.internal.web.controllers.support.RestServersResultCollector; |
| import org.apache.geode.rest.internal.web.exception.GemfireRestException; |
| import org.apache.geode.rest.internal.web.util.ArrayUtils; |
| import org.apache.geode.rest.internal.web.util.JSONUtils; |
| |
| /** |
| * The CommonCrudController serves REST Requests related to listing regions, listing keys in region, |
| * delete keys or delete all data in region. |
| * |
| * @since GemFire 8.0 |
| */ |
| @SuppressWarnings("unused") |
| public abstract class CommonCrudController extends AbstractBaseController { |
| |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** |
| * List all available resources (Regions) in the GemFire cluster |
| * |
| * @return JSON document containing result |
| */ |
| @RequestMapping(method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) |
| @ApiOperation(value = "list all resources (Regions)", |
| notes = "List all available resources (Regions) in the Geode cluster") |
| @ApiResponses({@ApiResponse(code = 200, message = "OK."), |
| @ApiResponse(code = 401, message = "Invalid Username or Password."), |
| @ApiResponse(code = 403, message = "Insufficient privileges for operation."), |
| @ApiResponse(code = 500, message = "GemFire throws an error or exception.")}) |
| @PreAuthorize("@securityService.authorize('DATA', 'READ')") |
| public ResponseEntity<?> regions() { |
| logger.debug("Listing all resources (Regions) in Geode..."); |
| final HttpHeaders headers = new HttpHeaders(); |
| headers.setLocation(toUri()); |
| final Set<Region<?, ?>> regions = new HashSet<>(); |
| for (InternalRegion region : getCache().getApplicationRegions()) { |
| regions.add(region); |
| } |
| String listRegionsAsJson = JSONUtils.formulateJsonForListRegions(regions, "regions"); |
| return new ResponseEntity<>(listRegionsAsJson, headers, HttpStatus.OK); |
| } |
| |
| /** |
| * List all keys for the given region in the GemFire cluster |
| * |
| * @param region gemfire region |
| * @return JSON document containing result |
| */ |
| @RequestMapping(method = RequestMethod.GET, value = "/{region}/keys", |
| produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) |
| @ApiOperation(value = "list all keys", notes = "List all keys in region") |
| @ApiResponses({@ApiResponse(code = 200, message = "OK"), |
| @ApiResponse(code = 401, message = "Invalid Username or Password."), |
| @ApiResponse(code = 403, message = "Insufficient privileges for operation."), |
| @ApiResponse(code = 404, message = "Region does not exist"), |
| @ApiResponse(code = 500, message = "GemFire throws an error or exception")}) |
| @PreAuthorize("@securityService.authorize('DATA', 'READ', #region)") |
| public ResponseEntity<?> keys(@PathVariable("region") String region) { |
| logger.debug("Reading all Keys in Region ({})...", region); |
| |
| region = decode(region); |
| |
| Object[] keys = getKeys(region, null); |
| |
| String listKeysAsJson = JSONUtils.formulateJsonForListKeys(keys, "keys"); |
| final HttpHeaders headers = new HttpHeaders(); |
| headers.setLocation(toUri(region, "keys")); |
| return new ResponseEntity<>(listKeysAsJson, headers, HttpStatus.OK); |
| } |
| |
| /** |
| * Delete data for single key or specific keys in region |
| * |
| * @param region gemfire region |
| * @param keys for which data is requested |
| * @return JSON document containing result |
| */ |
| @RequestMapping(method = RequestMethod.DELETE, value = "/{region}/{keys}", |
| produces = {MediaType.APPLICATION_JSON_VALUE}) |
| @ApiOperation(value = "delete data for key(s)", |
| notes = "Delete data for single key or specific keys in region") |
| @ApiResponses({@ApiResponse(code = 200, message = "OK"), |
| @ApiResponse(code = 401, message = "Invalid Username or Password."), |
| @ApiResponse(code = 403, message = "Insufficient privileges for operation."), |
| @ApiResponse(code = 404, message = "Region or key(s) does not exist"), |
| @ApiResponse(code = 500, message = "GemFire throws an error or exception")}) |
| @PreAuthorize("@securityService.authorize('WRITE', #region, #keys)") |
| public ResponseEntity<?> delete(@PathVariable("region") String region, |
| @PathVariable("keys") final String[] keys) { |
| logger.debug("Delete data for key {} on region {}", ArrayUtils.toString((Object[]) keys), |
| region); |
| |
| region = decode(region); |
| |
| deleteValues(region, (Object[]) keys); |
| return new ResponseEntity<>(HttpStatus.OK); |
| } |
| |
| /** |
| * Delete all data in region |
| * |
| * @param region gemfire region |
| * @return JSON document containing result |
| */ |
| @RequestMapping(method = RequestMethod.DELETE, value = "/{region}") |
| @ApiOperation(value = "delete all data", notes = "Delete all data in the region") |
| @ApiResponses({@ApiResponse(code = 200, message = "OK"), |
| @ApiResponse(code = 401, message = "Invalid Username or Password."), |
| @ApiResponse(code = 403, message = "Insufficient privileges for operation."), |
| @ApiResponse(code = 404, message = "Region does not exist"), |
| @ApiResponse(code = 500, message = "if GemFire throws an error or exception")}) |
| @PreAuthorize("@securityService.authorize('DATA', 'WRITE', #region)") |
| public ResponseEntity<?> delete(@PathVariable("region") String region) { |
| logger.debug("Deleting all data in Region ({})...", region); |
| |
| region = decode(region); |
| |
| deleteValues(region); |
| return new ResponseEntity<>(HttpStatus.OK); |
| } |
| |
| /** |
| * Ping is not secured so that it may not be used to determine a valid username/password |
| */ |
| @RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD}, value = "/ping") |
| @ApiOperation(value = "Check Rest service status ", |
| notes = "Check whether gemfire REST service is up and running!") |
| @ApiResponses({@ApiResponse(code = 200, message = "OK"), |
| @ApiResponse(code = 500, message = "if GemFire throws an error or exception")}) |
| public ResponseEntity<?> ping() { |
| return new ResponseEntity<>(HttpStatus.OK); |
| } |
| |
| @RequestMapping(method = {RequestMethod.GET}, value = "/servers", |
| produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) |
| @ApiOperation(value = "fetch all REST enabled servers in the DS", |
| notes = "Find all gemfire node where developer REST service is up and running!") |
| @ApiResponses({@ApiResponse(code = 200, message = "OK"), |
| @ApiResponse(code = 401, message = "Invalid Username or Password."), |
| @ApiResponse(code = 403, message = "Insufficient privileges for operation."), |
| @ApiResponse(code = 500, message = "if GemFire throws an error or exception")}) |
| @PreAuthorize("@securityService.authorize('CLUSTER', 'READ')") |
| public ResponseEntity<?> servers() { |
| logger.debug("Executing function to get REST enabled gemfire nodes in the DS!"); |
| |
| Execution function; |
| try { |
| function = FunctionService.onMembers(getAllMembersInDS()); |
| } catch (FunctionException fe) { |
| throw new GemfireRestException( |
| "Distributed system does not contain any valid data node that can host REST service!", |
| fe); |
| } |
| |
| try { |
| final ResultCollector<?, ?> results = function.withCollector(new RestServersResultCollector()) |
| .execute(FindRestEnabledServersFunction.FIND_REST_ENABLED_SERVERS_FUNCTION_ID); |
| Object functionResult = results.getResult(); |
| |
| if (functionResult instanceof List<?>) { |
| final HttpHeaders headers = new HttpHeaders(); |
| headers.setLocation(toUri("servers")); |
| String functionResultAsJson = |
| JSONUtils.convertCollectionToJson((ArrayList<Object>) functionResult); |
| return new ResponseEntity<>(functionResultAsJson, headers, HttpStatus.OK); |
| } else { |
| throw new GemfireRestException( |
| "Function has returned results that could not be converted into Restful (JSON) format!"); |
| } |
| |
| } catch (ClassCastException cce) { |
| throw new GemfireRestException("Key is of an inappropriate type for this region!", cce); |
| } catch (NullPointerException npe) { |
| throw new GemfireRestException( |
| "Specified key is null and this region does not permit null keys!", npe); |
| } catch (LowMemoryException lme) { |
| throw new GemfireRestException("Server has encountered low memory condition!", lme); |
| } catch (IllegalArgumentException ie) { |
| throw new GemfireRestException("Input parameter is null! ", ie); |
| } catch (FunctionException fe) { |
| throw new GemfireRestException("Server has encountered error while executing the function!", |
| fe); |
| } |
| } |
| } |