/*
* 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.pdx;
import static org.apache.geode.distributed.ConfigurationProperties.*;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Properties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.Scope;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.internal.Assert;
import org.apache.geode.internal.AvailablePortHelper;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.pdx.internal.json.PdxToJSON;
import org.apache.geode.test.dunit.Host;
import org.apache.geode.test.dunit.NetworkUtils;
import org.apache.geode.test.dunit.SerializableCallable;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase;
import org.apache.geode.test.junit.categories.DistributedTest;
import org.apache.geode.util.test.TestUtil;
@Category(DistributedTest.class)
public class JSONPdxClientServerDUnitTest extends JUnit4CacheTestCase {
@Override
public final void preTearDownCacheTestCase() {
// this test creates client caches in some VMs and so
// breaks the contract of CacheTestCase to hold caches in
// that class's "cache" instance variable
disconnectAllFromDS();
}
@Test
public void testSimplePut() {
Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
createServerRegion(vm0);
int port = createServerRegion(vm3);
createClientRegion(vm1, port);
createClientRegion(vm2, port);
vm1.invoke(new SerializableCallable() {
public Object call() throws Exception {
JSONAllStringTest();
return null;
}
});
vm2.invoke(new SerializableCallable() {
public Object call() throws Exception {
JSONAllByteArrayTest();
return null;
}
});
}
@Test
public void testSimplePutWithSortedJSONField() {
Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
createServerRegion(vm0);
int port = createServerRegion(vm3);
createClientRegion(vm1, port);
createClientRegion(vm2, port);
vm1.invoke(new SerializableCallable() {
public Object call() throws Exception {
try {
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY, "true");
JSONAllStringTest();
} finally {
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY, "false");
}
return null;
}
});
vm2.invoke(new SerializableCallable() {
public Object call() throws Exception {
try {
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY, "true");
JSONAllByteArrayTest();
} finally {
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY, "false");
}
return null;
}
});
}
// this is for unquote fielnames in json string
@Test
public void testSimplePut2() {
Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
createServerRegion(vm0);
int port = createServerRegion(vm3);
createClientRegion(vm1, port);
createClientRegion(vm2, port);
vm1.invoke(new SerializableCallable() {
public Object call() throws Exception {
JSONUnQuoteFields();
return null;
}
});
}
@Test
public void testPdxInstanceAndJSONConversion() {
Host host = Host.getHost(0);
VM vm0 = host.getVM(0);
VM vm1 = host.getVM(1);
VM vm2 = host.getVM(2);
VM vm3 = host.getVM(3);
createServerRegion(vm0, true);
int port = createServerRegion(vm3, true);
createClientRegion(vm1, port, false, true);
createClientRegion(vm2, port, false, true);
vm1.invoke(new SerializableCallable() {
public Object call() throws Exception {
VerifyPdxInstanceAndJsonConversion();
return null;
}
});
}
public void VerifyPdxInstanceAndJsonConversion() {
Region region = getRootRegion("testSimplePdx");
// Create Object and initialize its members.
TestObjectForJSONFormatter testObject = new TestObjectForJSONFormatter();
testObject.defaultInitialization();
// put the object into cache.
region.put("101", testObject);
// Get the object as PdxInstance
Object result = (Object) region.get("101");
if (result instanceof PdxInstance) {
PdxInstance pi = (PdxInstance) result;
String json = JSONFormatter.toJSON(pi);
try {
JSONObject jsonObject = new JSONObject(json);
// Testcase-1: Validate json string against the pdxInstance.
// validation for primitive types
assertEquals("VerifyPdxInstanceToJson: Int type values are not matched",
testObject.getP_int(), jsonObject.getInt(testObject.getP_intFN()));
assertEquals("VerifyPdxInstanceToJson: long type values are not matched",
testObject.getP_long(), jsonObject.getLong(testObject.getP_longFN()));
// validation for wrapper types
assertEquals("VerifyPdxInstanceToJson: Boolean type values are not matched",
testObject.getW_bool().booleanValue(), jsonObject.getBoolean(testObject.getW_boolFN()));
assertEquals("VerifyPdxInstanceToJson: Float type values are not matched",
testObject.getW_double().doubleValue(),
jsonObject.getDouble(testObject.getW_doubleFN()), 0);
assertEquals("VerifyPdxInstanceToJson: bigDec type values are not matched",
testObject.getW_bigDec().longValue(), jsonObject.getLong(testObject.getW_bigDecFN()));
// vlidation for array types
assertEquals("VerifyPdxInstanceToJson: Byte[] type values are not matched",
(int) testObject.getW_byteArray()[1],
jsonObject.getJSONArray(testObject.getW_byteArrayFN()).getInt(1));
assertEquals("VerifyPdxInstanceToJson: Double[] type values are not matched",
testObject.getW_doubleArray()[0],
jsonObject.getJSONArray(testObject.getW_doubleArrayFN()).getDouble(0), 0);
assertEquals("VerifyPdxInstanceToJson: String[] type values are not matched",
testObject.getW_strArray()[2],
jsonObject.getJSONArray(testObject.getW_strArrayFN()).getString(2));
// validation for collection types
assertEquals("VerifyPdxInstanceToJson: list type values are not matched",
testObject.getC_list().get(0),
jsonObject.getJSONArray(testObject.getC_listFN()).getString(0));
assertEquals("VerifyPdxInstanceToJson: stack type values are not matched",
testObject.getC_stack().get(2),
jsonObject.getJSONArray(testObject.getC_stackFN()).getString(2));
// validation for Map
assertEquals("VerifyPdxInstanceToJson: Map type values are not matched",
testObject.getM_empByCity().get("Ahmedabad").get(0).getFname(),
jsonObject.getJSONObject(testObject.getM_empByCityFN()).getJSONArray("Ahmedabad")
.getJSONObject(0).getString("fname"));
// validation Enum
assertEquals("VerifyPdxInstanceToJson: Enum type values are not matched",
testObject.getDay().toString(), jsonObject.getString(testObject.getDayFN()));
} catch (JSONException e) {
fail("Error in VerifyPdxInstanceToJson, Malformed json, can not create JSONArray from it");
}
} else {
fail("Error in VerifyPdxInstanceToJson, result must be of type PdxInstance");
}
// TestCase-2 : Validate Java-->JSON-->PdxInstance --> Java Mapping
TestObjectForJSONFormatter actualTestObject = new TestObjectForJSONFormatter();
actualTestObject.defaultInitialization();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("MM/dd/yyyy"));
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
// 1. get the json from the object using Jackosn Object Mapper
String json = objectMapper.writeValueAsString(actualTestObject);
String jsonWithClassType = actualTestObject.addClassTypeToJson(json);
// 2. convert json into the PdxInstance and put it into the region
PdxInstance pi = JSONFormatter.fromJSON(jsonWithClassType);
region.put("201", pi);
// 3. get the value on key "201" and validate PdxInstance.getObject() API.
Object receivedObject = region.get("201");
if (receivedObject instanceof PdxInstance) {
PdxInstance receivedPdxInstance = (PdxInstance) receivedObject;
// 4. get the actualType testObject from the pdxInstance and compare it with
// actualTestObject
Object getObj = receivedPdxInstance.getObject();
if (getObj instanceof TestObjectForJSONFormatter) {
TestObjectForJSONFormatter receivedTestObject = (TestObjectForJSONFormatter) getObj;
boolean isEqual = actualTestObject.equals(receivedTestObject);
Assert.assertTrue(isEqual, "actualTestObject and receivedTestObject should be equal");
} else {
fail("getObj is expected to be an instance of TestObjectForJSONFormatter");
}
} else {
fail("receivedObject is expected to be of type PdxInstance");
}
} catch (JsonProcessingException e) {
fail("JsonProcessingException: error encountered while converting JSON from Java object: "
+ e.getMessage());
} catch (JSONException e) {
fail("JSONException: error encountered while adding @type classType into Json: "
+ e.getMessage());
}
}
String getJSONDir(String file) {
String path = TestUtil.getResourcePath(getClass(), file);
return new File(path).getParent();
}
public void JSONUnQuoteFields() {
System.setProperty("pdxToJson.unqouteFieldNames", "true");
PdxToJSON.PDXTOJJSON_UNQUOTEFIELDNAMES = true;
String jsonStringsDir =
getJSONDir("/org/apache/geode/pdx/jsonStrings/unquoteJsonStrings/json1.txt");
JSONAllStringTest(jsonStringsDir);
PdxToJSON.PDXTOJJSON_UNQUOTEFIELDNAMES = false;
}
public void JSONAllStringTest() {
String jsonStringsDir = getJSONDir("jsonStrings/json1.txt");
JSONAllStringTest(jsonStringsDir);
}
public void JSONAllStringTest(String dirname) {
JSONData[] allJsons = loadAllJSON(dirname);
int i = 0;
for (JSONData jsonData : allJsons) {
if (jsonData != null) {
i++;
VerifyJSONString(jsonData);
}
}
Assert.assertTrue(i >= 1, "Number of files should be more than 10 : " + i);
}
public void JSONAllByteArrayTest() {
String jsonStringsDir = getJSONDir("jsonStrings/json1.txt");
JSONData[] allJsons = loadAllJSON(jsonStringsDir);
int i = 0;
for (JSONData jsonData : allJsons) {
if (jsonData != null) {
i++;
VerifyJSONByteArray(jsonData);
}
}
Assert.assertTrue(i > 10, "Number of files should be more than 10");
}
static class JSONData {
String jsonFileName;
byte[] jsonByteArray;
public JSONData(String fn, byte[] js) {
jsonFileName = fn;
jsonByteArray = js;
}
public String getFileName() {
return jsonFileName;
}
public String getJsonString() {
return new String(jsonByteArray);
}
public byte[] getJsonByteArray() {
return jsonByteArray;
}
}
public void VerifyJSONString(JSONData jd) {
Region r = getRootRegion("testSimplePdx");
PdxInstance pdx = JSONFormatter.fromJSON(jd.getJsonString());
r.put(1, pdx);
pdx = (PdxInstance) r.get(1);
String getJsonString = JSONFormatter.toJSON(pdx);
String o1 = jsonParse(jd.getJsonString());
String o2 = jsonParse(getJsonString);
if (!Boolean.getBoolean(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY)) {
assertEquals("Json Strings are not equal " + jd.getFileName() + " "
+ Boolean.getBoolean("pdxToJson.unqouteFieldNames"), o1, o2);
} else {
// we just need to compare length as blob will be different because fields are sorted
assertEquals("Json Strings are not equal " + jd.getFileName() + " "
+ Boolean.getBoolean("pdxToJson.unqouteFieldNames"), o1.length(), o2.length());
}
PdxInstance pdx2 = JSONFormatter.fromJSON(getJsonString);
assertEquals("Pdx are not equal; json filename " + jd.getFileName(), pdx, pdx2);
}
protected final static int INT_TAB = '\t';
protected final static int INT_LF = '\n';
protected final static int INT_CR = '\r';
protected final static int INT_SPACE = 0x0020;
public String jsonParse(String jsonSting) {
byte[] ba = jsonSting.getBytes();
byte[] withoutspace = new byte[ba.length];
int i = 0;
int j = 0;
for (i = 0; i < ba.length; i++) {
int cbyte = ba[i];
if (cbyte == INT_TAB || cbyte == INT_LF || cbyte == INT_CR || cbyte == INT_SPACE)
continue;
withoutspace[j++] = ba[i];
}
return new String(withoutspace, 0, j);
}
public void VerifyJSONByteArray(JSONData jd) {
Region r = getRootRegion("testSimplePdx");
PdxInstance pdx = JSONFormatter.fromJSON(jd.getJsonByteArray());
r.put(1, pdx);
pdx = (PdxInstance) r.get(1);
byte[] jsonByteArray = JSONFormatter.toJSONByteArray(pdx);
byte[] o1 = jsonParse(jd.getJsonByteArray());
byte[] o2 = jsonParse(jsonByteArray);
compareByteArray(o1, o2);
PdxInstance pdx2 = JSONFormatter.fromJSON(jsonByteArray);
boolean pdxequals = pdx.equals(pdx2);
assertEquals("Pdx are not equal for byte array ; json filename " + jd.getFileName(), pdx, pdx2);
}
public void compareByteArray(byte[] b1, byte[] b2) {
if (b1.length != b2.length)
throw new IllegalStateException(
"Json byte array length are not equal " + b1.length + " ; " + b2.length);
if (Boolean.getBoolean(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY))
return;// we just need to compare length as blob will be different because fields are sorted
for (int i = 0; i < b1.length; i++) {
if (b1[i] != b2[i])
throw new IllegalStateException("Json byte arrays are not equal ");
}
}
public byte[] jsonParse(byte[] jsonBA) {
byte[] ba = jsonBA;
byte[] withoutspace = new byte[ba.length];
int i = 0;
int j = 0;
for (i = 0; i < ba.length; i++) {
int cbyte = ba[i];
if (cbyte == INT_TAB || cbyte == INT_LF || cbyte == INT_CR || cbyte == INT_SPACE)
continue;
withoutspace[j++] = ba[i];
}
byte[] retBA = new byte[j];
for (i = 0; i < j; i++) {
retBA[i] = withoutspace[i];
}
return retBA;
}
public static JSONData[] loadAllJSON(String jsondir) {
File dir = new File(jsondir);
JSONData[] JSONDatas = new JSONData[dir.list().length];
int i = 0;
for (String jsonFileName : dir.list()) {
if (!jsonFileName.contains(".txt"))
continue;
try {
byte[] ba = getBytesFromFile(dir.getAbsolutePath() + File.separator + jsonFileName);
JSONDatas[i++] = new JSONData(jsonFileName, ba);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return JSONDatas;
}
public static byte[] getBytesFromFile(String fileName) throws IOException {
File file = new File(fileName);
java.io.InputStream is = new FileInputStream(file);
// Get the size of the file
long length = file.length();
// Create the byte array to hold the data
byte[] bytes = new byte[(int) length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file " + file.getName());
}
is.close();
return bytes;
}
private void closeCache(VM vm) {
vm.invoke(new SerializableCallable() {
public Object call() throws Exception {
closeCache();
return null;
}
});
}
private int createServerRegion(VM vm) {
SerializableCallable createRegion = new SerializableCallable() {
public Object call() throws Exception {
AttributesFactory af = new AttributesFactory();
// af.setScope(Scope.DISTRIBUTED_ACK);
af.setDataPolicy(DataPolicy.PARTITION);
createRootRegion("testSimplePdx", af.create());
CacheServer server = getCache().addCacheServer();
int port = AvailablePortHelper.getRandomAvailableTCPPort();
server.setPort(port);
server.start();
return port;
}
};
return (Integer) vm.invoke(createRegion);
}
private int createServerRegion(VM vm, final boolean isPdxReadSerialized) {
SerializableCallable createRegion = new SerializableCallable() {
public Object call() throws Exception {
AttributesFactory af = new AttributesFactory();
// af.setScope(Scope.DISTRIBUTED_ACK);
af.setDataPolicy(DataPolicy.PARTITION);
createRootRegion("testSimplePdx", af.create());
((GemFireCacheImpl) getCache()).getCacheConfig().setPdxReadSerialized(isPdxReadSerialized);
CacheServer server = getCache().addCacheServer();
int port = AvailablePortHelper.getRandomAvailableTCPPort();
server.setPort(port);
server.start();
return port;
}
};
return (Integer) vm.invoke(createRegion);
}
private int createServerRegionWithPersistence(VM vm, final boolean persistentPdxRegistry) {
SerializableCallable createRegion = new SerializableCallable() {
public Object call() throws Exception {
CacheFactory cf = new CacheFactory();
if (persistentPdxRegistry) {
cf.setPdxPersistent(true).setPdxDiskStore("store");
}
//
Cache cache = getCache(cf);
cache.createDiskStoreFactory().setDiskDirs(getDiskDirs()).create("store");
AttributesFactory af = new AttributesFactory();
af.setScope(Scope.DISTRIBUTED_ACK);
af.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE);
af.setDiskStoreName("store");
createRootRegion("testSimplePdx", af.create());
CacheServer server = getCache().addCacheServer();
int port = AvailablePortHelper.getRandomAvailableTCPPort();
server.setPort(port);
server.start();
return port;
}
};
return (Integer) vm.invoke(createRegion);
}
private int createServerAccessor(VM vm) {
SerializableCallable createRegion = new SerializableCallable() {
public Object call() throws Exception {
AttributesFactory af = new AttributesFactory();
// af.setScope(Scope.DISTRIBUTED_ACK);
af.setDataPolicy(DataPolicy.EMPTY);
createRootRegion("testSimplePdx", af.create());
CacheServer server = getCache().addCacheServer();
int port = AvailablePortHelper.getRandomAvailableTCPPort();
server.setPort(port);
server.start();
return port;
}
};
return (Integer) vm.invoke(createRegion);
}
private int createLonerServerRegion(VM vm, final String regionName, final String dsId) {
SerializableCallable createRegion = new SerializableCallable() {
public Object call() throws Exception {
Properties props = new Properties();
props.setProperty(LOCATORS, "");
props.setProperty(DISTRIBUTED_SYSTEM_ID, dsId);
getSystem(props);
AttributesFactory af = new AttributesFactory();
af.setScope(Scope.DISTRIBUTED_ACK);
af.setDataPolicy(DataPolicy.REPLICATE);
createRootRegion(regionName, af.create());
CacheServer server = getCache().addCacheServer();
int port = AvailablePortHelper.getRandomAvailableTCPPort();
server.setPort(port);
server.start();
return port;
}
};
return (Integer) vm.invoke(createRegion);
}
private void createClientRegion(final VM vm, final int port) {
createClientRegion(vm, port, false);
}
private void createClientRegion(final VM vm, final int port,
final boolean threadLocalConnections) {
SerializableCallable createRegion = new SerializableCallable() {
public Object call() throws Exception {
ClientCacheFactory cf = new ClientCacheFactory();
cf.addPoolServer(NetworkUtils.getServerHostName(vm.getHost()), port);
cf.setPoolThreadLocalConnections(threadLocalConnections);
ClientCache cache = getClientCache(cf);
cache.createClientRegionFactory(ClientRegionShortcut.PROXY).create("testSimplePdx");
return null;
}
};
vm.invoke(createRegion);
}
private void createClientRegion(final VM vm, final int port, final boolean threadLocalConnections,
final boolean isPdxReadSerialized) {
SerializableCallable createRegion = new SerializableCallable() {
public Object call() throws Exception {
ClientCacheFactory cf = new ClientCacheFactory();
cf.addPoolServer(NetworkUtils.getServerHostName(vm.getHost()), port);
cf.setPoolThreadLocalConnections(threadLocalConnections);
cf.setPdxReadSerialized(isPdxReadSerialized);
ClientCache cache = getClientCache(cf);
cache.createClientRegionFactory(ClientRegionShortcut.PROXY).create("testSimplePdx");
return null;
}
};
vm.invoke(createRegion);
}
}