/*
* 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 com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.geode.SerializationException;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheLoaderException;
import org.apache.geode.cache.CacheWriterException;
import org.apache.geode.cache.LowMemoryException;
import org.apache.geode.cache.PartitionedRegionStorageException;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.TimeoutException;
import org.apache.geode.cache.query.Query;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.LeaseExpiredException;
import org.apache.geode.distributed.internal.DistributionManager;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.pdx.JSONFormatter;
import org.apache.geode.pdx.JSONFormatterException;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.rest.internal.web.controllers.support.JSONTypes;
import org.apache.geode.rest.internal.web.controllers.support.UpdateOp;
import org.apache.geode.rest.internal.web.exception.DataTypeNotSupportedException;
import org.apache.geode.rest.internal.web.exception.GemfireRestException;
import org.apache.geode.rest.internal.web.exception.MalformedJsonException;
import org.apache.geode.rest.internal.web.exception.RegionNotFoundException;
import org.apache.geode.rest.internal.web.exception.ResourceNotFoundException;
import org.apache.geode.rest.internal.web.security.RestSecurityService;
import org.apache.geode.rest.internal.web.util.ArrayUtils;
import org.apache.geode.rest.internal.web.util.IdentifiableUtils;
import org.apache.geode.rest.internal.web.util.JSONUtils;
import org.apache.geode.rest.internal.web.util.NumberUtils;
import org.apache.geode.rest.internal.web.util.ValidationUtils;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.PostConstruct;
/**
* AbstractBaseController class contains common functionalities required for other controllers.
*
* @since GemFire 8.0
*/
@SuppressWarnings("unused")
public abstract class AbstractBaseController {
protected static final String NEW_META_DATA_PROPERTY = "@new";
protected static final String OLD_META_DATA_PROPERTY = "@old";
protected static final String TYPE_META_DATA_PROPERTY = "@type";
protected static final String UTF_8 = "UTF-8";
protected static final String DEFAULT_ENCODING = UTF_8;
private static final Logger logger = LogService.getLogger();
private static final AtomicLong ID_SEQUENCE = new AtomicLong(0l);
// private Cache cache = GemFireCacheImpl.getExisting(null);
@Autowired
protected RestSecurityService securityService;
@Autowired
private ObjectMapper objectMapper;
@PostConstruct
private void init() {
JSONUtils.setObjectMapper(objectMapper);
}
protected Cache getCache() {
Cache cache = GemFireCacheImpl.getExisting();
Assert.state(cache != null, "The Gemfire Cache reference was not properly initialized");
return cache;
}
protected URI toUri(final String... pathSegments) {
return ServletUriComponentsBuilder.fromCurrentContextPath().path(getRestApiVersion())
.pathSegment(pathSegments).build().toUri();
}
protected abstract String getRestApiVersion();
protected String validateQuery(String queryInUrl, String queryInBody) {
if (!(StringUtils.hasText(queryInUrl) || StringUtils.hasText(queryInBody))) {
throw new GemfireRestException("could not process null value specified in query String");
}
return (StringUtils.hasText(queryInUrl) ? decode(queryInUrl) : queryInBody);
}
protected String decode(final String value) {
if (value == null) {
throw new GemfireRestException("could not process null value specified in query String");
}
return decode(value, DEFAULT_ENCODING);
}
protected PdxInstance convert(final String json) {
try {
return (StringUtils.hasText(json) ? JSONFormatter.fromJSON(json) : null);
} catch (JSONFormatterException jpe) {
throw new MalformedJsonException("Json doc specified is either not supported or invalid!",
jpe);
}
}
protected String convert(final PdxInstance pdxObj) {
try {
return (pdxObj != null ? JSONFormatter.toJSON(pdxObj) : null);
} catch (JSONFormatterException jpe) {
throw new GemfireRestException("Requested data could not convert into REST format(JSON)!",
jpe);
}
}
protected String convert(final Iterable<PdxInstance> pdxObjs) {
final StringBuilder buffer = new StringBuilder("[");
int count = 0;
for (final PdxInstance pdxObj : pdxObjs) {
final String json = convert(pdxObj);
if (StringUtils.hasText(json)) {
buffer.append(count++ > 0 ? ", " : "").append(json);
}
}
buffer.append("]");
return buffer.toString();
}
@SuppressWarnings("unchecked")
protected <T> T casValue(String regionNamePath, String key, String jsonData) {
JSONObject jsonObject;
try {
jsonObject = new JSONObject(jsonData);
String oldValue = jsonObject.get("@old").toString();
String newValue = jsonObject.get("@new").toString();
return (T) casValue(regionNamePath, key, convert(oldValue), convert(newValue));
} catch (JSONException je) {
throw new MalformedJsonException("Json doc specified in request body is invalid!", je);
}
}
public ResponseEntity<String> processQueryResponse(Query query, Object args[], Object queryResult)
throws JSONException {
if (queryResult instanceof Collection<?>) {
Collection processedResults = new ArrayList(((Collection) queryResult).size());
for (Object result : (Collection) queryResult) {
processedResults.add(securityService.postProcess(null, null, result, false));
}
String queryResultAsJson = JSONUtils.convertCollectionToJson(processedResults);
final HttpHeaders headers = new HttpHeaders();
headers.setLocation(toUri("queries", query.getQueryString()));
return new ResponseEntity<>(queryResultAsJson, headers, HttpStatus.OK);
} else {
throw new GemfireRestException(
"Server has encountered error while generating query result into restful format(JSON)!");
}
}
protected Collection<PdxInstance> convertJsonArrayIntoPdxCollection(final String jsonArray) {
JSONArray jsonArr = null;
try {
jsonArr = new JSONArray(jsonArray);
Collection<PdxInstance> pdxInstances = new ArrayList<PdxInstance>();
for (int index = 0; index < jsonArr.length(); index++) {
// String element = jsonArr.getJSONObject(i).toString();
// String element = jsonArr.getString(i);
Object object = jsonArr.get(index);
String element = object.toString();
PdxInstance pi = convert(element);
pdxInstances.add(pi);
}
return pdxInstances;
} catch (JSONException je) {
throw new MalformedJsonException("Json document specified in request body is not valid!", je);
}
}
/*
* protected PdxInstance convertJsonIntoPdxCollection(final String jsonArray) { JSONArray jsonArr
* = null;
*
* PdxInstance pi = convert(jsonArray);
* System.out.println("Successfully converted into PdxInstance..!!"); return pi;
*
* }
*/
protected Object casValue(final String regionNamePath, final Object key, final Object oldValue,
final Object newValue) {
final Region<Object, Object> region = getRegion(regionNamePath);
try {
return (region.replace(key, oldValue, newValue) ? null : region.get(key));
} catch (UnsupportedOperationException use) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not support the requested operation!",
regionNamePath),
use);
} catch (ClassCastException cce) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration does not allow to store specified key or value type in this region!",
regionNamePath), cce);
} catch (NullPointerException npe) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not allow null keys or values!",
regionNamePath),
npe);
} catch (IllegalArgumentException iae) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration prevents specified data from being stored in it!",
regionNamePath), iae);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException toe) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", toe);
} catch (CacheWriterException cwe) {
throw new GemfireRestException(
"Server has encountered CacheWriter error while processing this request!", cwe);
} catch (PartitionedRegionStorageException prse) {
throw new GemfireRestException(
"Requested operation could not be completed on a partitioned region!", prse);
} catch (LowMemoryException lme) {
throw new GemfireRestException(
"Server has detected low memory while processing this request!", lme);
}
}
protected void replaceValue(final String regionNamePath, final Object key,
final PdxInstance value) {
try {
if (getRegion(regionNamePath).replace(key, value) == null) {
throw new ResourceNotFoundException(String.format("No resource at (%1$s) exists!",
toUri(regionNamePath, String.valueOf(key))));
}
} catch (UnsupportedOperationException use) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not support the requested operation!",
regionNamePath),
use);
} catch (ClassCastException cce) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration does not allow to store specified key or value type in this region!",
regionNamePath), cce);
} catch (NullPointerException npe) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not allow null keys or values!",
regionNamePath),
npe);
} catch (IllegalArgumentException iae) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration prevents specified data from being stored in it!",
regionNamePath), iae);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException toe) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", toe);
} catch (CacheWriterException cwe) {
throw new GemfireRestException(
"Server has encountered CacheWriter error while processing this request!", cwe);
} catch (PartitionedRegionStorageException prse) {
throw new GemfireRestException(
"Requested operation could not be completed on a partitioned region!", prse);
} catch (LowMemoryException lme) {
throw new GemfireRestException(
"Server has detected low memory while processing this request!", lme);
}
}
protected void replaceValue(final String regionNamePath, final Object key, final Object value) {
// still do we need to worry for race condition..?
try {
if (getRegion(regionNamePath).replace(key, value) == null) {
throw new ResourceNotFoundException(String.format("No resource at (%1$s) exists!",
toUri(regionNamePath, String.valueOf(key))));
}
} catch (UnsupportedOperationException use) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not support the requested operation!",
regionNamePath),
use);
} catch (ClassCastException cce) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration does not allow to store specified key or value type in this region!",
regionNamePath), cce);
} catch (NullPointerException npe) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not allow null keys or values!",
regionNamePath),
npe);
} catch (IllegalArgumentException iae) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration prevents specified data from being stored in it!",
regionNamePath), iae);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException toe) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", toe);
} catch (CacheWriterException cwe) {
throw new GemfireRestException(
"Server has encountered CacheWriter error while processing this request!", cwe);
} catch (PartitionedRegionStorageException prse) {
throw new GemfireRestException(
"Requested operation could not be completed on a partitioned region!", prse);
} catch (LowMemoryException lme) {
throw new GemfireRestException(
"Server has detected low memory while processing this request!", lme);
}
}
protected void putValue(final String regionNamePath, final Object key, final Object value) {
try {
getRegion(regionNamePath).put(key, value);
} catch (NullPointerException npe) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not allow null keys or values!",
regionNamePath),
npe);
} catch (ClassCastException cce) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration does not allow to store specified key or value type in this region!",
regionNamePath), cce);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException toe) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", toe);
} catch (CacheWriterException cwe) {
throw new GemfireRestException(
"Server has encountered CacheWriter error while processing this request!", cwe);
} catch (PartitionedRegionStorageException prse) {
throw new GemfireRestException(
"Requested operation could not be completed on a partitioned region!", prse);
} catch (LowMemoryException lme) {
throw new GemfireRestException(
"Server has detected low memory while processing this request!", lme);
}
}
protected void deleteQueryId(final String regionNamePath, final String key) {
getQueryStore(regionNamePath).remove(key);
}
protected void deleteNamedQuery(final String regionNamePath, final String key) {
// Check whether query ID exist in region or not
checkForQueryIdExist(regionNamePath, key);
deleteQueryId(regionNamePath, key);
}
protected void checkForQueryIdExist(String region, String key) {
if (!getQueryStore(region).containsKey(key)) {
throw new ResourceNotFoundException(String.format("Named query (%1$s) does not exist!", key));
}
}
protected Region<String, String> getQueryStore(final String namePath) {
return ValidationUtils.returnValueThrowOnNull(getCache().<String, String>getRegion(namePath),
new GemfireRestException(String.format("Query store does not exist!", namePath)));
}
protected String getQueryIdValue(final String regionNamePath, final String key) {
Assert.notNull(key, "queryId cannot be null!");
try {
return getQueryStore(regionNamePath).get(key);
} catch (NullPointerException npe) {
throw new GemfireRestException("NULL query ID or query string is not supported!", npe);
} catch (IllegalArgumentException iae) {
throw new GemfireRestException("Server has not allowed to perform the requested operation!",
iae);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException te) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", te);
}
}
protected void updateNamedQuery(final String regionNamePath, final String key,
final String value) {
try {
getQueryStore(regionNamePath).put(key, value);
} catch (NullPointerException npe) {
throw new GemfireRestException("NULL query ID or query string is not supported!", npe);
} catch (ClassCastException cce) {
throw new GemfireRestException("specified queryId or query string is not supported!", cce);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException toe) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", toe);
} catch (LowMemoryException lme) {
throw new GemfireRestException(
"Server has detected low memory while processing this request!", lme);
}
}
@SuppressWarnings("unchecked")
protected <T> T createNamedQuery(final String regionNamePath, final String key,
final String value) {
try {
return (T) getQueryStore(regionNamePath).putIfAbsent(key, value);
} catch (UnsupportedOperationException use) {
throw new GemfireRestException("Requested operation is not supported!", use);
} catch (ClassCastException cce) {
throw new GemfireRestException("Specified queryId or query string is not supported!", cce);
} catch (NullPointerException npe) {
throw new GemfireRestException("NULL query ID or query string is not supported!", npe);
} catch (IllegalArgumentException iae) {
throw new GemfireRestException(
"Configuration does not allow to perform the requested operation!", iae);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException toe) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", toe);
} catch (LowMemoryException lme) {
throw new GemfireRestException(
"Server has detected low memory while processing this request!", lme);
}
}
protected void putPdxValues(final String regionNamePath, final Map<Object, PdxInstance> map) {
try {
getRegion(regionNamePath).putAll(map);
} catch (LowMemoryException lme) {
throw new GemfireRestException("low memory condition is detected.", lme);
}
}
protected void putValues(final String regionNamePath, final Map<Object, Object> values) {
getRegion(regionNamePath).putAll(values);
}
protected void putValues(final String regionNamePath, String[] keys, List<?> values) {
Map<Object, Object> map = new HashMap<Object, Object>();
if (keys.length != values.size()) {
throw new GemfireRestException("Bad request, Keys and Value size does not match");
}
for (int i = 0; i < keys.length; i++) {
Object domainObj = introspectAndConvert(values.get(i));
map.put(keys[i], domainObj);
}
if (!map.isEmpty()) {
putValues(regionNamePath, map);
}
}
@SuppressWarnings("unchecked")
protected <T> T postValue(final String regionNamePath, final Object key, final Object value) {
try {
return (T) getRegion(regionNamePath).putIfAbsent(key, value);
} catch (UnsupportedOperationException use) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not support the requested operation!",
regionNamePath),
use);
} catch (ClassCastException cce) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration does not allow to store specified key or value type in this region!",
regionNamePath), cce);
} catch (NullPointerException npe) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not allow null keys or values!",
regionNamePath),
npe);
} catch (IllegalArgumentException iae) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration prevents specified data from being stored in it!",
regionNamePath), iae);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException toe) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", toe);
} catch (CacheWriterException cwe) {
throw new GemfireRestException(
"Server has encountered CacheWriter error while processing this request!", cwe);
} catch (PartitionedRegionStorageException prse) {
throw new GemfireRestException(
"Requested operation could not be completed on a partitioned region!", prse);
} catch (LowMemoryException lme) {
throw new GemfireRestException(
"Server has detected low memory while processing this request!", lme);
}
}
protected Object getActualTypeValue(final String value, final String valueType) {
Object actualValue = value;
if (valueType != null) {
try {
actualValue = NumberUtils.convertToActualType(value, valueType);
} catch (IllegalArgumentException ie) {
throw new GemfireRestException(ie.getMessage(), ie);
}
}
return actualValue;
}
protected String generateKey(final String existingKey) {
return generateKey(existingKey, null);
}
protected String generateKey(final String existingKey, final Object domainObject) {
Object domainObjectId = IdentifiableUtils.getId(domainObject);
String newKey;
if (StringUtils.hasText(existingKey)) {
newKey = existingKey;
if (NumberUtils.isNumeric(newKey) && domainObjectId == null) {
final Long newId = IdentifiableUtils.createId(NumberUtils.parseLong(newKey));
if (newKey.equals(newId.toString())) {
IdentifiableUtils.setId(domainObject, newId);
}
}
} else if (domainObjectId != null) {
final Long domainObjectIdAsLong = NumberUtils.longValue(domainObjectId);
if (domainObjectIdAsLong != null) {
final Long newId = IdentifiableUtils.createId(domainObjectIdAsLong);
if (!domainObjectIdAsLong.equals(newId)) {
IdentifiableUtils.setId(domainObject, newId);
}
newKey = String.valueOf(newId);
} else {
newKey = String.valueOf(domainObjectId);
}
} else {
domainObjectId = IdentifiableUtils.createId();
newKey = String.valueOf(domainObjectId);
IdentifiableUtils.setId(domainObject, domainObjectId);
}
return newKey;
}
protected String decode(final String value, final String encoding) {
try {
return URLDecoder.decode(value, encoding);
} catch (UnsupportedEncodingException e) {
throw new GemfireRestException("Server has encountered unsupported encoding!");
}
}
@SuppressWarnings("unchecked")
protected <T> Region<Object, T> getRegion(final String namePath) {
return (Region<Object, T>) ValidationUtils
.returnValueThrowOnNull(getCache().getRegion(namePath), new RegionNotFoundException(
String.format("The Region identified by name (%1$s) could not be found!", namePath)));
}
protected void checkForKeyExist(String region, String key) {
if (!getRegion(region).containsKey(key)) {
throw new ResourceNotFoundException(
String.format("Key (%1$s) does not exist for region (%2$s) in cache!", key, region));
}
}
protected List<String> checkForMultipleKeysExist(String region, String... keys) {
List<String> unknownKeys = new ArrayList<String>();
for (int index = 0; index < keys.length; index++) {
if (!getRegion(region).containsKey(keys[index])) {
// throw new ResourceNotFoundException(String.format("Key [(%1$s)] does not exist for region
// [(%2$s)] in cache!", key, region));
unknownKeys.add(keys[index]);
}
}
return unknownKeys;
}
protected Object[] getKeys(final String regionNamePath, Object[] keys) {
return (!(keys == null || keys.length == 0) ? keys
: getRegion(regionNamePath).keySet().toArray());
}
protected <T> Map<Object, T> getValues(final String regionNamePath, Object... keys) {
try {
final Region<Object, T> region = getRegion(regionNamePath);
final Map<Object, T> entries = region.getAll(Arrays.asList(getKeys(regionNamePath, keys)));
for (Object key : entries.keySet()) {
entries.put(key,
(T) securityService.postProcess(regionNamePath, key, entries.get(key), false));
}
return entries;
} catch (SerializationException se) {
throw new DataTypeNotSupportedException(
"The resource identified could not convert into the supported content characteristics (JSON)!",
se);
}
}
protected <T> Map<Object, T> getValues(final String regionNamePath, String... keys) {
return getValues(regionNamePath, (Object[]) keys);
}
protected <T extends PdxInstance> Collection<T> getPdxValues(final String regionNamePath,
final Object... keys) {
final Region<Object, T> region = getRegion(regionNamePath);
final Map<Object, T> entries = region.getAll(Arrays.asList(getKeys(regionNamePath, keys)));
return entries.values();
}
protected void deleteValue(final String regionNamePath, final Object key) {
getRegion(regionNamePath).remove(key);
}
protected void deleteValues(final String regionNamePath, final Object... keys) {
// Check whether all keys exist in cache or not
for (final Object key : keys) {
checkForKeyExist(regionNamePath, key.toString());
}
for (final Object key : keys) {
deleteValue(regionNamePath, key);
}
}
protected void deleteValues(String regionNamePath) {
try {
getRegion(regionNamePath).clear();
} catch (UnsupportedOperationException ue) {
throw new GemfireRestException("Requested operation not allowed on partition region", ue);
}
}
private boolean isForm(final Map<Object, Object> rawDataBinding) {
return (!rawDataBinding.containsKey(TYPE_META_DATA_PROPERTY)
&& rawDataBinding.containsKey(OLD_META_DATA_PROPERTY)
&& rawDataBinding.containsKey(NEW_META_DATA_PROPERTY));
}
@SuppressWarnings("unchecked")
protected <T> T introspectAndConvert(final T value) {
if (value instanceof Map) {
final Map rawDataBinding = (Map) value;
if (isForm(rawDataBinding)) {
rawDataBinding.put(OLD_META_DATA_PROPERTY,
introspectAndConvert(rawDataBinding.get(OLD_META_DATA_PROPERTY)));
rawDataBinding.put(NEW_META_DATA_PROPERTY,
introspectAndConvert(rawDataBinding.get(NEW_META_DATA_PROPERTY)));
return (T) rawDataBinding;
} else {
final String typeValue = (String) rawDataBinding.get(TYPE_META_DATA_PROPERTY);
// Added for the primitive types put. Not supporting primitive types
if (NumberUtils.isPrimitiveOrObject(typeValue.toString())) {
final Object primitiveValue = rawDataBinding.get("@value");
try {
return (T) NumberUtils.convertToActualType(primitiveValue.toString(), typeValue);
} catch (IllegalArgumentException e) {
throw new GemfireRestException(
"Server has encountered error (illegal or inappropriate arguments).", e);
}
} else {
Assert.state(typeValue != null,
"The class type of the object to persist in GemFire must be specified in JSON content using the '@type' property!");
Assert.state(
ClassUtils.isPresent(String.valueOf(typeValue),
Thread.currentThread().getContextClassLoader()),
String.format("Class (%1$s) could not be found!", typeValue));
return (T) objectMapper.convertValue(rawDataBinding, ClassUtils.resolveClassName(
String.valueOf(typeValue), Thread.currentThread().getContextClassLoader()));
}
}
}
return value;
}
protected String convertErrorAsJson(String errorMessage) {
return ("{" + "\"cause\"" + ":" + "\"" + errorMessage + "\"" + "}");
}
protected String convertErrorAsJson(Throwable t) {
StringWriter writer = new StringWriter();
t.printStackTrace(new PrintWriter(writer));
return String.format("{\"message\" : \"%1$s\", \"stackTrace\" : \"%2$s\"}", t.getMessage(),
writer.toString());
}
protected Map<?, ?> convertJsonToMap(final String jsonString) {
Map<String, String> map = new HashMap<String, String>();
// convert JSON string to Map
try {
map = objectMapper.readValue(jsonString, new TypeReference<HashMap<String, String>>() {});
} catch (JsonParseException e) {
throw new MalformedJsonException(
"Bind params specified as JSON document in the request is incorrect!", e);
} catch (JsonMappingException e) {
throw new MalformedJsonException(
"Server unable to process bind params specified as JSON document in the request!", e);
} catch (IOException e) {
throw new GemfireRestException("Server has encountered error while process this request!", e);
}
return map;
}
protected Object jsonToObject(final String jsonString) {
return introspectAndConvert(convertJsonToMap(jsonString));
}
protected Object[] jsonToObjectArray(final String arguments) {
final JSONTypes jsonType = validateJsonAndFindType(arguments);
if (JSONTypes.JSON_ARRAY.equals(jsonType)) {
try {
JSONArray jsonArray = new JSONArray(arguments);
Object[] args = new Object[jsonArray.length()];
for (int index = 0; index < jsonArray.length(); index++) {
args[index] = jsonToObject(jsonArray.get(index).toString());
}
return args;
} catch (JSONException je) {
throw new MalformedJsonException("Json document specified in request body is not valid!",
je);
}
} else if (JSONTypes.JSON_OBJECT.equals(jsonType)) {
return new Object[] {jsonToObject(arguments)};
} else {
throw new MalformedJsonException("Json document specified in request body is not valid!");
}
}
public ResponseEntity<String> updateSingleKey(final String region, final String key,
final String json, final String opValue) {
final JSONTypes jsonType = validateJsonAndFindType(json);
final UpdateOp op = UpdateOp.valueOf(opValue.toUpperCase());
String existingValue = null;
switch (op) {
case CAS:
PdxInstance existingPdxObj = casValue(region, key, json);
existingValue = convert(existingPdxObj);
break;
case REPLACE:
replaceValue(region, key, convert(json));
break;
case PUT:
default:
if (JSONTypes.JSON_ARRAY.equals(jsonType)) {
putValue(region, key, convertJsonArrayIntoPdxCollection(json));
// putValue(region, key, convertJsonIntoPdxCollection(json));
} else {
putValue(region, key, convert(json));
}
}
final HttpHeaders headers = new HttpHeaders();
headers.setLocation(toUri(region, key));
return new ResponseEntity<String>(existingValue, headers,
(existingValue == null ? HttpStatus.OK : HttpStatus.CONFLICT));
}
public ResponseEntity<String> updateMultipleKeys(final String region, final String[] keys,
final String json) {
JSONArray jsonArr = null;
try {
jsonArr = new JSONArray(json);
} catch (JSONException e) {
throw new MalformedJsonException("JSON document specified in the request is incorrect", e);
}
if (jsonArr.length() != keys.length) {
throw new MalformedJsonException(
"Each key must have corresponding value (JSON document) specified in the request");
}
Map<Object, PdxInstance> map = new HashMap<Object, PdxInstance>();
for (int i = 0; i < keys.length; i++) {
if (logger.isDebugEnabled()) {
logger.debug("Updating (put) Json document ({}) having key ({}) in Region ({})", json,
keys[i], region);
}
try {
PdxInstance pdxObj = convert(jsonArr.getJSONObject(i).toString());
map.put(keys[i], pdxObj);
} catch (JSONException e) {
throw new MalformedJsonException(
String.format("JSON document at index (%1$s) in the request body is incorrect", i), e);
}
}
if (!CollectionUtils.isEmpty(map)) {
putPdxValues(region, map);
}
HttpHeaders headers = new HttpHeaders();
headers.setLocation(toUri(region, StringUtils.arrayToCommaDelimitedString(keys)));
return new ResponseEntity<String>(headers, HttpStatus.OK);
}
public JSONTypes validateJsonAndFindType(String json) {
try {
Object jsonObj = new JSONTokener(json).nextValue();
if (jsonObj instanceof JSONObject) {
return JSONTypes.JSON_OBJECT;
} else if (jsonObj instanceof JSONArray) {
return JSONTypes.JSON_ARRAY;
} else {
return JSONTypes.UNRECOGNIZED_JSON;
}
} catch (JSONException je) {
throw new MalformedJsonException("JSON document specified in the request is incorrect", je);
}
}
protected QueryService getQueryService() {
return getCache().getQueryService();
}
@SuppressWarnings("unchecked")
protected <T> T getValue(final String regionNamePath, final Object key) {
return getValue(regionNamePath, key, true);
}
protected <T> T getValue(final String regionNamePath, final Object key, boolean postProcess) {
Assert.notNull(key, "The Cache Region key to read the value for cannot be null!");
Region r = getRegion(regionNamePath);
try {
Object value = r.get(key);
if (postProcess) {
return (T) securityService.postProcess(regionNamePath, key, value, false);
} else {
return (T) value;
}
} catch (SerializationException se) {
throw new DataTypeNotSupportedException(
"The resource identified could not convert into the supported content characteristics (JSON)!",
se);
} catch (NullPointerException npe) {
throw new GemfireRestException(
String.format("Resource (%1$s) configuration does not allow null keys!", regionNamePath),
npe);
} catch (IllegalArgumentException iae) {
throw new GemfireRestException(String.format(
"Resource (%1$s) configuration does not allow requested operation on specified key!",
regionNamePath), iae);
} catch (LeaseExpiredException lee) {
throw new GemfireRestException("Server has encountered error while processing this request!",
lee);
} catch (TimeoutException te) {
throw new GemfireRestException(
"Server has encountered timeout error while processing this request!", te);
} catch (CacheLoaderException cle) {
throw new GemfireRestException(
"Server has encountered CacheLoader error while processing this request!", cle);
} catch (PartitionedRegionStorageException prse) {
throw new GemfireRestException("CacheLoader could not be invoked on partitioned region!",
prse);
}
}
protected Set<DistributedMember> getMembers(final String... memberIdNames) {
ValidationUtils.returnValueThrowOnNull(memberIdNames,
new GemfireRestException("No member found to run function"));
final Set<DistributedMember> targetedMembers =
new HashSet<DistributedMember>(ArrayUtils.length(memberIdNames));
final List<String> memberIdNameList = Arrays.asList(memberIdNames);
GemFireCacheImpl c = (GemFireCacheImpl) getCache();
Set<DistributedMember> distMembers = c.getDistributedSystem().getAllOtherMembers();
// Add the local node to list
distMembers.add(c.getDistributedSystem().getDistributedMember());
for (DistributedMember member : distMembers) {
if (memberIdNameList.contains(member.getId())
|| memberIdNameList.contains(member.getName())) {
targetedMembers.add(member);
}
}
return targetedMembers;
}
protected Set<DistributedMember> getAllMembersInDS() {
GemFireCacheImpl c = (GemFireCacheImpl) getCache();
Set<DistributedMember> distMembers = c.getDistributedSystem().getAllOtherMembers();
final Set<DistributedMember> targetedMembers = new HashSet<DistributedMember>();
// find valid data nodes, i.e non locator, non-admin, non-loner nodes
for (DistributedMember member : distMembers) {
InternalDistributedMember idm = (InternalDistributedMember) member;
if (idm.getVmKind() == DistributionManager.NORMAL_DM_TYPE) {
targetedMembers.add(member);
}
}
// Add the local node to list
targetedMembers.add(c.getDistributedSystem().getDistributedMember());
return targetedMembers;
}
}