Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 943F1200CBA for ; Mon, 19 Jun 2017 05:58:11 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 92B96160BF0; Mon, 19 Jun 2017 03:58:11 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 1B1B1160BEE for ; Mon, 19 Jun 2017 05:58:08 +0200 (CEST) Received: (qmail 69815 invoked by uid 500); 19 Jun 2017 03:58:08 -0000 Mailing-List: contact commits-help@ignite.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ignite.apache.org Delivered-To: mailing list commits@ignite.apache.org Received: (qmail 69798 invoked by uid 99); 19 Jun 2017 03:58:08 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 19 Jun 2017 03:58:08 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id DB378DFAF5; Mon, 19 Jun 2017 03:58:07 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: irudyak@apache.org To: commits@ignite.apache.org Date: Mon, 19 Jun 2017 03:58:07 -0000 Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: [1/6] ignite git commit: ignite-5368 archived-at: Mon, 19 Jun 2017 03:58:11 -0000 Repository: ignite Updated Branches: refs/heads/master 25d529714 -> 6b29143a3 ignite-5368 Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/d44fdd45 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/d44fdd45 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/d44fdd45 Branch: refs/heads/master Commit: d44fdd452125a7735ec4718b18a7dcea5a95f471 Parents: fbcd474 Author: Igor Rudyak Authored: Fri Jun 2 22:54:15 2017 -0700 Committer: Igor Rudyak Committed: Fri Jun 2 22:54:15 2017 -0700 ---------------------------------------------------------------------- .../cassandra/common/PropertyMappingHelper.java | 73 +++----- .../persistence/KeyPersistenceSettings.java | 155 +++++----------- .../persistence/PersistenceSettings.java | 83 ++++++++- .../store/cassandra/persistence/PojoField.java | 129 +++++++------ .../persistence/PojoFieldAccessor.java | 162 ++++++++++++++++ .../cassandra/persistence/PojoKeyField.java | 13 +- .../cassandra/persistence/PojoValueField.java | 12 +- .../persistence/ValuePersistenceSettings.java | 50 +---- .../store/cassandra/utils/DDLGenerator.java | 9 +- .../tests/CassandraDirectPersistenceTest.java | 73 +++++++- .../apache/ignite/tests/DDLGeneratorTest.java | 4 + .../ignite/tests/IgnitePersistentStoreTest.java | 85 ++++++++- .../apache/ignite/tests/pojos/SimplePerson.java | 186 +++++++++++++++++++ .../ignite/tests/pojos/SimplePersonId.java | 89 +++++++++ .../apache/ignite/tests/utils/TestsHelper.java | 97 +++++++++- .../tests/persistence/pojo/ignite-config.xml | 36 ++++ .../persistence/pojo/persistence-settings-5.xml | 21 +++ .../persistence/pojo/persistence-settings-6.xml | 174 +++++++++++++++++ 18 files changed, 1158 insertions(+), 293 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/common/PropertyMappingHelper.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/common/PropertyMappingHelper.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/common/PropertyMappingHelper.java index cb89bf0..66d7294 100644 --- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/common/PropertyMappingHelper.java +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/common/PropertyMappingHelper.java @@ -20,17 +20,17 @@ package org.apache.ignite.cache.store.cassandra.common; import com.datastax.driver.core.DataType; import com.datastax.driver.core.Row; import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.beanutils.PropertyUtils; +import org.apache.ignite.cache.store.cassandra.persistence.PojoFieldAccessor; import org.apache.ignite.cache.store.cassandra.serializer.Serializer; /** @@ -77,63 +77,38 @@ public class PropertyMappingHelper { } /** - * Returns property descriptor by class property name. + * Returns property accessor by class property name. * - * @param clazz class from which to get property descriptor. + * @param clazz class from which to get property accessor. * @param prop name of the property. * - * @return property descriptor. + * @return property accessor. */ - public static PropertyDescriptor getPojoPropertyDescriptor(Class clazz, String prop) { - List descriptors = getPojoPropertyDescriptors(clazz, false); - - if (descriptors == null || descriptors.isEmpty()) - throw new IllegalArgumentException("POJO class " + clazz.getName() + " doesn't have '" + prop + "' property"); - - for (PropertyDescriptor descriptor : descriptors) { - if (descriptor.getName().equals(prop)) - return descriptor; - } - - throw new IllegalArgumentException("POJO class " + clazz.getName() + " doesn't have '" + prop + "' property"); - } - - /** - * Extracts all property descriptors from a class. - * - * @param clazz class which property descriptors should be extracted. - * @param primitive boolean flag indicating that only property descriptors for primitive properties - * should be extracted. - * - * @return list of class property descriptors - */ - public static List getPojoPropertyDescriptors(Class clazz, boolean primitive) { + public static PojoFieldAccessor getPojoFieldAccessor(Class clazz, String prop) { PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(clazz); - List list = new ArrayList<>(descriptors == null ? 1 : descriptors.length); + if (descriptors != null) { + for (PropertyDescriptor descriptor : descriptors) { + if (descriptor.getName().equals(prop)) { + Field field = null; - if (descriptors == null || descriptors.length == 0) - return list; + try { + field = clazz.getDeclaredField(prop); + } + catch (Throwable ignore) { + } - for (PropertyDescriptor descriptor : descriptors) { - if (descriptor.getReadMethod() == null || (primitive && !isPrimitivePropertyDescriptor(descriptor))) - continue; - - list.add(descriptor); + return new PojoFieldAccessor(descriptor, field); + } + } } - return list; - } - - /** - * Checks if property descriptor describes primitive property (int, boolean, long and etc.) - * - * @param desc property descriptor. - * - * @return {@code true} property is primitive - */ - public static boolean isPrimitivePropertyDescriptor(PropertyDescriptor desc) { - return PropertyMappingHelper.JAVA_TO_CASSANDRA_MAPPING.containsKey(desc.getPropertyType()); + try { + return new PojoFieldAccessor(clazz.getDeclaredField(prop)); + } + catch (Throwable e) { + throw new IllegalArgumentException("POJO class " + clazz.getName() + " doesn't have '" + prop + "' property"); + } } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java index c12c3e8..2f2e18e 100644 --- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/KeyPersistenceSettings.java @@ -17,12 +17,10 @@ package org.apache.ignite.cache.store.cassandra.persistence; -import java.beans.PropertyDescriptor; import java.util.LinkedList; import java.util.List; -import org.apache.ignite.IgniteException; -import org.apache.ignite.cache.store.cassandra.common.PropertyMappingHelper; +import org.apache.ignite.cache.affinity.AffinityKeyMapped; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -62,26 +60,53 @@ public class KeyPersistenceSettings extends PersistenceSettings { return; } - NodeList keyElem = el.getElementsByTagName(PARTITION_KEY_ELEMENT); + Element node = el.getElementsByTagName(PARTITION_KEY_ELEMENT) != null ? + (Element)el.getElementsByTagName(PARTITION_KEY_ELEMENT).item(0) : null; - Element partKeysNode = keyElem != null ? (Element) keyElem.item(0) : null; + NodeList partKeysNodes = node == null ? null : node.getElementsByTagName(FIELD_ELEMENT); - Element clusterKeysNode = el.getElementsByTagName(CLUSTER_KEY_ELEMENT) != null ? - (Element)el.getElementsByTagName(CLUSTER_KEY_ELEMENT).item(0) : null; + node = el.getElementsByTagName(CLUSTER_KEY_ELEMENT) != null ? + (Element)el.getElementsByTagName(CLUSTER_KEY_ELEMENT).item(0) : null; - if (partKeysNode == null && clusterKeysNode != null) { + NodeList clusterKeysNodes = node == null ? null : node.getElementsByTagName(FIELD_ELEMENT); + + if ((partKeysNodes == null || partKeysNodes.getLength() == 0) && + clusterKeysNodes != null && clusterKeysNodes.getLength() > 0) { throw new IllegalArgumentException("It's not allowed to specify cluster key fields mapping, but " + "doesn't specify partition key mappings"); } - partKeyFields = detectFields(partKeysNode, getPartitionKeyDescriptors()); + // Detecting partition key fields + partKeyFields = detectPojoFields(partKeysNodes); if (partKeyFields == null || partKeyFields.isEmpty()) { throw new IllegalStateException("Failed to initialize partition key fields for class '" + - getJavaClass().getName() + "'"); + getJavaClass().getName() + "'"); + } + + List filteredFields = new LinkedList<>(); + + // Find all fields annotated by @AffinityKeyMapped + for (PojoField field : partKeyFields) { + if (field.getAnnotation(AffinityKeyMapped.class) != null) + filteredFields.add(field); } - clusterKeyFields = detectFields(clusterKeysNode, getClusterKeyDescriptors(partKeyFields)); + // If there are any fields annotated by @AffinityKeyMapped then all other fields are part of cluster key + partKeyFields = !filteredFields.isEmpty() ? filteredFields : partKeyFields; + + // Detecting cluster key fields + clusterKeyFields = detectPojoFields(clusterKeysNodes); + + filteredFields = new LinkedList<>(); + + // Removing out all fields which are already in partition key fields list + for (PojoField field : clusterKeyFields) { + if (!PojoField.containsField(partKeyFields, field.getName())) + filteredFields.add(field); + } + + clusterKeyFields = filteredFields; fields = new LinkedList<>(); fields.addAll(partKeyFields); @@ -97,6 +122,16 @@ public class KeyPersistenceSettings extends PersistenceSettings { return fields; } + /** {@inheritDoc} */ + @Override protected PojoField createPojoField(Element el, Class clazz) { + return new PojoKeyField(el, clazz); + } + + /** {@inheritDoc} */ + @Override protected PojoField createPojoField(PojoFieldAccessor accessor) { + return new PojoKeyField(accessor); + } + /** * Returns Cassandra DDL for primary key. * @@ -196,102 +231,4 @@ public class KeyPersistenceSettings extends PersistenceSettings { return cols; } - - /** - * Extracts POJO fields specified in XML element. - * - * @param el XML element describing fields. - * @param descriptors POJO fields descriptors. - * @return List of {@code This} fields. - */ - private List detectFields(Element el, List descriptors) { - List list = new LinkedList<>(); - - if (el == null && (descriptors == null || descriptors.isEmpty())) - return list; - - if (el == null) { - for (PropertyDescriptor desc : descriptors) { - // Skip POJO field if it's read-only - if (desc.getWriteMethod() != null) - list.add(new PojoKeyField(desc)); - } - - return list; - } - - NodeList nodes = el.getElementsByTagName(FIELD_ELEMENT); - - int cnt = nodes == null ? 0 : nodes.getLength(); - - if (cnt == 0) { - throw new IllegalArgumentException("Incorrect configuration of Cassandra key persistence settings, " + - "no key fields specified inside '" + PARTITION_KEY_ELEMENT + "/" + - CLUSTER_KEY_ELEMENT + "' element"); - } - - for (int i = 0; i < cnt; i++) { - PojoKeyField field = new PojoKeyField((Element)nodes.item(i), getJavaClass()); - - PropertyDescriptor desc = findPropertyDescriptor(descriptors, field.getName()); - - if (desc == null) { - throw new IllegalArgumentException("Specified POJO field '" + field.getName() + - "' doesn't exist in '" + getJavaClass().getName() + "' class"); - } - - list.add(field); - } - - return list; - } - - /** - * @return POJO field descriptors for partition key. - */ - private List getPartitionKeyDescriptors() { - List primitivePropDescriptors = PropertyMappingHelper.getPojoPropertyDescriptors( - getJavaClass(), true); - - boolean valid = false; - - for (PropertyDescriptor desc : primitivePropDescriptors) { - if (desc.getWriteMethod() != null) { - valid = true; - - break; - } - } - - if (!valid) { - throw new IgniteException("Partition key can't have only calculated read-only fields, there should be " + - "some fields with setter method"); - } - - return primitivePropDescriptors; - } - - /** - * @param partKeyFields List of fields. - * @return POJO field descriptors for cluster key. - */ - private List getClusterKeyDescriptors(List partKeyFields) { - List primitivePropDescriptors = - PropertyMappingHelper.getPojoPropertyDescriptors(getJavaClass(), true); - - if (primitivePropDescriptors == null || primitivePropDescriptors.isEmpty() || - partKeyFields.size() == primitivePropDescriptors.size()) - return null; - - for (PojoField field : partKeyFields) { - for (int i = 0; i < primitivePropDescriptors.size(); i++) { - if (primitivePropDescriptors.get(i).getName().equals(field.getName())) { - primitivePropDescriptors.remove(i); - break; - } - } - } - - return primitivePropDescriptors; - } } http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java index f22c0a4..220ef1f 100644 --- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PersistenceSettings.java @@ -20,19 +20,19 @@ package org.apache.ignite.cache.store.cassandra.persistence; import com.datastax.driver.core.DataType; import java.beans.PropertyDescriptor; import java.io.Serializable; +import java.lang.reflect.Field; import java.nio.ByteBuffer; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Collections; +import java.util.*; +import org.apache.commons.beanutils.PropertyUtils; import org.apache.ignite.IgniteException; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.apache.ignite.cache.store.cassandra.common.CassandraHelper; import org.apache.ignite.cache.store.cassandra.common.PropertyMappingHelper; import org.apache.ignite.cache.store.cassandra.serializer.JavaSerializer; import org.apache.ignite.cache.store.cassandra.serializer.Serializer; import org.w3c.dom.Element; +import org.w3c.dom.NodeList; /** * Stores persistence settings, which describes how particular key/value @@ -327,6 +327,21 @@ public abstract class PersistenceSettings implements Serializable { protected abstract String defaultColumnName(); /** + * Creates instance of {@link PojoField} based on it's description in XML element. + * + * @param el XML element describing POJO field + * @param clazz POJO java class. + */ + protected abstract PojoField createPojoField(Element el, Class clazz); + + /** + * Creates instance of {@link PojoField} from its field accessor. + * + * @param accessor field accessor. + */ + protected abstract PojoField createPojoField(PojoFieldAccessor accessor); + + /** * Class instance initialization. */ protected void init() { @@ -395,6 +410,63 @@ public abstract class PersistenceSettings implements Serializable { } /** + * Extracts POJO fields from a list of corresponding XML field nodes. + * + * @param fieldNodes Field nodes to process. + * @return POJO fields list. + */ + protected List detectPojoFields(NodeList fieldNodes) { + List detectedFields = new LinkedList<>(); + + if (fieldNodes != null && fieldNodes.getLength() != 0) { + int cnt = fieldNodes.getLength(); + + for (int i = 0; i < cnt; i++) { + PojoField field = createPojoField((Element)fieldNodes.item(i), getJavaClass()); + + // Just checking that such field exists in the class + PropertyMappingHelper.getPojoFieldAccessor(getJavaClass(), field.getName()); + + detectedFields.add(field); + } + + return detectedFields; + } + + PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(getJavaClass()); + + // Collecting Java Beans property descriptors + if (descriptors != null) { + for (PropertyDescriptor desc : descriptors) { + // Skip POJO field if it's read-only + if (desc.getWriteMethod() != null) { + Field field = null; + + try { + field = getJavaClass().getDeclaredField(desc.getName()); + } + catch (Throwable ignore) { + } + + detectedFields.add(createPojoField(new PojoFieldAccessor(desc, field))); + } + } + } + + Field[] fields = getJavaClass().getDeclaredFields(); + + // Collecting all fields annotated with @QuerySqlField + if (fields != null) { + for (Field field : fields) { + if (field.getAnnotation(QuerySqlField.class) != null && !PojoField.containsField(detectedFields, field.getName())) + detectedFields.add(createPojoField(new PojoFieldAccessor(field))); + } + } + + return detectedFields; + } + + /** * Instantiates Class object for particular class * * @param clazz class name @@ -442,5 +514,4 @@ public abstract class PersistenceSettings implements Serializable { throw new IgniteException("Failed to instantiate class '" + clazz + "' using default constructor", e); } } - } http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java index 566c0b9..386628b 100644 --- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoField.java @@ -19,10 +19,12 @@ package org.apache.ignite.cache.store.cassandra.persistence; import com.datastax.driver.core.DataType; import com.datastax.driver.core.Row; -import java.beans.PropertyDescriptor; import java.io.Serializable; -import org.apache.ignite.IgniteException; +import java.lang.annotation.Annotation; +import java.util.List; + import org.apache.ignite.cache.store.cassandra.common.PropertyMappingHelper; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.apache.ignite.cache.store.cassandra.serializer.Serializer; import org.w3c.dom.Element; @@ -40,9 +42,6 @@ public abstract class PojoField implements Serializable { /** Field name. */ private String name; - /** Java class to which the field belongs. */ - private Class objJavaCls; - /** Field column name in Cassandra table. */ private String col; @@ -52,8 +51,27 @@ public abstract class PojoField implements Serializable { /** Indicator for calculated field. */ private Boolean calculated; - /** Field property descriptor. */ - private transient PropertyDescriptor desc; + /** Field property accessor. */ + private transient PojoFieldAccessor accessor; + + /** + * Checks if list contains POJO field with the specified name. + * + * @param fields list of POJO fields. + * @param fieldName field name. + * @return true if list contains field or false otherwise. + */ + public static boolean containsField(List fields, String fieldName) { + if (fields == null || fields.isEmpty()) + return false; + + for (PojoField field : fields) { + if (field.getName().equals(fieldName)) + return true; + } + + return false; + } /** * Creates instance of {@link PojoField} based on it's description in XML element. @@ -73,20 +91,23 @@ public abstract class PojoField implements Serializable { this.name = el.getAttribute(NAME_ATTR).trim(); this.col = el.hasAttribute(COLUMN_ATTR) ? el.getAttribute(COLUMN_ATTR).trim() : name.toLowerCase(); - init(PropertyMappingHelper.getPojoPropertyDescriptor(pojoCls, name)); + init(PropertyMappingHelper.getPojoFieldAccessor(pojoCls, name)); } /** - * Creates instance of {@link PojoField} from its property descriptor. + * Creates instance of {@link PojoField} from its field accessor. * - * @param desc Field property descriptor. + * @param accessor field accessor. */ - public PojoField(PropertyDescriptor desc) { - this.name = desc.getName(); + public PojoField(PojoFieldAccessor accessor) { + this.name = accessor.getName(); - col = name.toLowerCase(); + QuerySqlField sqlField = (QuerySqlField)accessor.getAnnotation(QuerySqlField.class); - init(desc); + col = sqlField != null && sqlField.name() != null && !sqlField.name().isEmpty() ? + sqlField.name() : name.toLowerCase(); + + init(accessor); } /** @@ -102,7 +123,7 @@ public abstract class PojoField implements Serializable { * @return Java class. */ public Class getJavaClass() { - return propDesc().getPropertyType(); + return accessor.getFieldType(); } /** @@ -131,7 +152,7 @@ public abstract class PojoField implements Serializable { if (calculated != null) return calculated; - return calculated = propDesc().getWriteMethod() == null; + return calculated = accessor.isReadOnly(); } /** @@ -143,28 +164,31 @@ public abstract class PojoField implements Serializable { * @return Object to store in Cassandra table column. */ public Object getValueFromObject(Object obj, Serializer serializer) { - try { - Object val = propDesc().getReadMethod().invoke(obj); - - if (val == null) - return null; + Object val = accessor.getValue(obj); - DataType.Name cassandraType = PropertyMappingHelper.getCassandraType(val.getClass()); + if (val == null) + return null; - if (cassandraType != null) - return val; + DataType.Name cassandraType = PropertyMappingHelper.getCassandraType(val.getClass()); - if (serializer == null) { - throw new IllegalStateException("Can't serialize value from object '" + - val.getClass().getName() + "' field '" + name + "', cause there is no BLOB serializer specified"); - } + if (cassandraType != null) + return val; - return serializer.serialize(val); - } - catch (Throwable e) { - throw new IgniteException("Failed to get value of the field '" + name + "' from the instance " + - " of '" + obj.getClass().toString() + "' class", e); + if (serializer == null) { + throw new IllegalStateException("Can't serialize value from object '" + + val.getClass().getName() + "' field '" + name + "', cause there is no BLOB serializer specified"); } + + return serializer.serialize(val); + } + + /** + * Returns POJO field annotation. + * + * @return annotation. + */ + public Annotation getAnnotation(Class clazz) { + return accessor.getAnnotation(clazz); } /** @@ -178,49 +202,22 @@ public abstract class PojoField implements Serializable { if (calculatedField()) return; - Object val = PropertyMappingHelper.getCassandraColumnValue(row, col, propDesc().getPropertyType(), serializer); + Object val = PropertyMappingHelper.getCassandraColumnValue(row, col, accessor.getFieldType(), serializer); - try { - propDesc().getWriteMethod().invoke(obj, val); - } - catch (Throwable e) { - throw new IgniteException("Failed to set value of the field '" + name + "' of the instance " + - " of '" + obj.getClass().toString() + "' class", e); - } + accessor.setValue(obj, val); } /** * Initializes field info from property descriptor. * - * @param desc {@link PropertyDescriptor} descriptor. + * @param accessor {@link PojoFieldAccessor} accessor. */ - protected void init(PropertyDescriptor desc) { - if (desc.getReadMethod() == null) { - throw new IllegalArgumentException("Field '" + desc.getName() + - "' of the class instance '" + desc.getPropertyType().getName() + - "' doesn't provide getter method"); - } - - if (!desc.getReadMethod().isAccessible()) - desc.getReadMethod().setAccessible(true); - - if (desc.getWriteMethod() != null && !desc.getWriteMethod().isAccessible()) - desc.getWriteMethod().setAccessible(true); - - DataType.Name cassandraType = PropertyMappingHelper.getCassandraType(desc.getPropertyType()); + private void init(PojoFieldAccessor accessor) { + DataType.Name cassandraType = PropertyMappingHelper.getCassandraType(accessor.getFieldType()); cassandraType = cassandraType == null ? DataType.Name.BLOB : cassandraType; - this.objJavaCls = desc.getReadMethod().getDeclaringClass(); - this.desc = desc; this.colDDL = "\"" + col + "\" " + cassandraType.toString(); - } - /** - * Returns property descriptor of the POJO field - * - * @return Property descriptor - */ - private PropertyDescriptor propDesc() { - return desc != null ? desc : (desc = PropertyMappingHelper.getPojoPropertyDescriptor(objJavaCls, name)); + this.accessor = accessor; } } http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoFieldAccessor.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoFieldAccessor.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoFieldAccessor.java new file mode 100644 index 0000000..f3d8a76 --- /dev/null +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoFieldAccessor.java @@ -0,0 +1,162 @@ +/* + * 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.ignite.cache.store.cassandra.persistence; + +import org.apache.ignite.IgniteException; + +import java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.List; + +/** + * Property accessor provides read/write access to POJO object properties defined through: + * 1) Getter/setter methods + * 2) Raw class members + */ +public class PojoFieldAccessor { + /** Java Bean property descriptor */ + private PropertyDescriptor desc; + + /** Object field associated with property descriptor. Used just to get annotations which + * applied not to property descriptor, but directly to object field associated with the property. */ + private Field descField; + + /** Object field */ + private Field field; + + /** + * Constructs object instance from Java Bean property descriptor, providing access to getter/setter. + * + * @param desc Java Bean property descriptor. + * @param field object field associated with property descriptor. + */ + public PojoFieldAccessor(PropertyDescriptor desc, Field field) { + if (desc.getReadMethod() == null) { + throw new IllegalArgumentException("Field '" + desc.getName() + + "' of the class instance '" + desc.getPropertyType().getName() + + "' doesn't provide getter method"); + } + + desc.getReadMethod().setAccessible(true); + + if (desc.getWriteMethod() != null) + desc.getWriteMethod().setAccessible(true); + + this.desc = desc; + this.descField = field; + } + + /** + * Constructs object instance from Field, providing direct access to class member. + * + * @param field Field descriptor. + */ + public PojoFieldAccessor(Field field) { + field.setAccessible(true); + this.field = field; + } + + /** + * Returns POJO field name. + * + * @return field name. + */ + public String getName() { + return desc != null ? desc.getName() : field.getName(); + } + + /** + * Indicates if it's read-only field. + * + * @return true if field read-only, false if not. + */ + public boolean isReadOnly() { + return desc != null && desc.getWriteMethod() == null; + } + + /** + * Returns POJO field annotation. + * + * @return annotation. + */ + public Annotation getAnnotation(Class clazz) { + if (field != null) + return field.getAnnotation(clazz); + + Annotation ann = desc.getReadMethod().getAnnotation(clazz); + + if (ann != null) + return ann; + + ann = desc.getWriteMethod() == null ? null : desc.getWriteMethod().getAnnotation(clazz); + + if (ann != null) + return ann; + + return descField == null ? null : descField.getAnnotation(clazz); + } + + /** + * Returns field value for the object instance. + * + * @param obj object instance. + * @return field value. + */ + public Object getValue(Object obj) { + try { + return desc != null ? desc.getReadMethod().invoke(obj) : field.get(obj); + } + catch (Throwable e) { + throw new IgniteException("Failed to get value of the field '" + getName() + "' from the instance " + + " of '" + obj.getClass().toString() + "' class", e); + } + } + + /** + * Assigns value for the object field. + * + * @param obj object instance. + * @param val value to assign. + */ + public void setValue(Object obj, Object val) { + if (isReadOnly()) + throw new IgniteException("Can't assign value to read-only field '" + getName() + "' of the instance " + + " of '" + obj.getClass().toString() + "' class"); + + try { + if (desc != null) + desc.getWriteMethod().invoke(obj, val); + else + field.set(obj, val); + } + catch (Throwable e) { + throw new IgniteException("Failed to set value of the field '" + getName() + "' of the instance " + + " of '" + obj.getClass().toString() + "' class", e); + } + } + + /** + * Returns field type. + * + * @return field type. + */ + public Class getFieldType() { + return desc != null ? desc.getPropertyType() : field.getType(); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java index bf1d40e..52b4584 100644 --- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoKeyField.java @@ -17,7 +17,7 @@ package org.apache.ignite.cache.store.cassandra.persistence; -import java.beans.PropertyDescriptor; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.w3c.dom.Element; /** @@ -63,10 +63,15 @@ public class PojoKeyField extends PojoField { /** * Constructs Ignite cache key POJO object descriptor. * - * @param desc property descriptor. + * @param accessor property descriptor. */ - public PojoKeyField(PropertyDescriptor desc) { - super(desc); + public PojoKeyField(PojoFieldAccessor accessor) { + super(accessor); + + QuerySqlField sqlField = (QuerySqlField)accessor.getAnnotation(QuerySqlField.class); + + if (sqlField != null && sqlField.descending()) + sortOrder = SortOrder.DESC; } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java index 9d25b60..36bcd0c 100644 --- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/PojoValueField.java @@ -17,7 +17,7 @@ package org.apache.ignite.cache.store.cassandra.persistence; -import java.beans.PropertyDescriptor; +import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.w3c.dom.Element; /** @@ -80,10 +80,14 @@ public class PojoValueField extends PojoField { /** * Constructs Ignite cache value field descriptor. * - * @param desc field property descriptor. + * @param accessor field property accessor. */ - public PojoValueField(PropertyDescriptor desc) { - super(desc); + public PojoValueField(PojoFieldAccessor accessor) { + super(accessor); + + QuerySqlField sqlField = (QuerySqlField)accessor.getAnnotation(QuerySqlField.class); + + isIndexed = sqlField != null && sqlField.index(); } /** {@inheritDoc} */ http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java index b737e2c..5824b6f 100644 --- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/persistence/ValuePersistenceSettings.java @@ -17,12 +17,10 @@ package org.apache.ignite.cache.store.cassandra.persistence; -import java.beans.PropertyDescriptor; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import org.apache.ignite.cache.store.cassandra.common.PropertyMappingHelper; import org.w3c.dom.Element; import org.w3c.dom.NodeList; @@ -52,7 +50,7 @@ public class ValuePersistenceSettings extends PersistenceSettings { NodeList nodes = el.getElementsByTagName(FIELD_ELEMENT); - fields = detectFields(nodes); + fields = detectPojoFields(nodes); if (fields.isEmpty()) throw new IllegalStateException("Failed to initialize value fields for class '" + getJavaClass().getName() + "'"); @@ -74,45 +72,13 @@ public class ValuePersistenceSettings extends PersistenceSettings { return "value"; } - /** - * Extracts POJO fields from a list of corresponding XML field nodes. - * - * @param fieldNodes Field nodes to process. - * @return POJO fields list. - */ - private List detectFields(NodeList fieldNodes) { - List list = new LinkedList<>(); - - if (fieldNodes == null || fieldNodes.getLength() == 0) { - List primitivePropDescriptors = - PropertyMappingHelper.getPojoPropertyDescriptors(getJavaClass(), true); - - for (PropertyDescriptor desc : primitivePropDescriptors) { - // Skip POJO field if it's read-only - if (desc.getWriteMethod() != null) - list.add(new PojoValueField(desc)); - } - - return list; - } - - List allPropDescriptors = PropertyMappingHelper.getPojoPropertyDescriptors(getJavaClass(), false); - - int cnt = fieldNodes.getLength(); - - for (int i = 0; i < cnt; i++) { - PojoValueField field = new PojoValueField((Element)fieldNodes.item(i), getJavaClass()); - - PropertyDescriptor desc = findPropertyDescriptor(allPropDescriptors, field.getName()); - - if (desc == null) { - throw new IllegalArgumentException("Specified POJO field '" + field.getName() + - "' doesn't exist in '" + getJavaClass().getName() + "' class"); - } - - list.add(field); - } + /** {@inheritDoc} */ + @Override protected PojoField createPojoField(Element el, Class clazz) { + return new PojoValueField(el, clazz); + } - return list; + /** {@inheritDoc} */ + @Override protected PojoField createPojoField(PojoFieldAccessor accessor) { + return new PojoValueField(accessor); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/utils/DDLGenerator.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/utils/DDLGenerator.java b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/utils/DDLGenerator.java index e3ec391..569c65d 100644 --- a/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/utils/DDLGenerator.java +++ b/modules/cassandra/store/src/main/java/org/apache/ignite/cache/store/cassandra/utils/DDLGenerator.java @@ -35,9 +35,12 @@ public class DDLGenerator { if (args == null || args.length == 0) return; + boolean success = true; + for (String arg : args) { File file = new File(arg); if (!file.isFile()) { + success = false; System.out.println("-------------------------------------------------------------"); System.out.println("Incorrect file specified: " + arg); System.out.println("-------------------------------------------------------------"); @@ -66,11 +69,15 @@ public class DDLGenerator { } } catch (Throwable e) { + success = false; System.out.println("-------------------------------------------------------------"); - System.out.println("Incorrect file specified: " + arg); + System.out.println("Invalid file specified: " + arg); System.out.println("-------------------------------------------------------------"); e.printStackTrace(); } } + + if (!success) + throw new RuntimeException("Failed to process some of the specified files"); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/java/org/apache/ignite/tests/CassandraDirectPersistenceTest.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/CassandraDirectPersistenceTest.java b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/CassandraDirectPersistenceTest.java index f9e9649..5cce9df 100644 --- a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/CassandraDirectPersistenceTest.java +++ b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/CassandraDirectPersistenceTest.java @@ -23,10 +23,7 @@ import java.util.Map; import org.apache.ignite.cache.store.CacheStore; import org.apache.ignite.internal.processors.cache.CacheEntryImpl; import org.apache.ignite.internal.util.typedef.internal.U; -import org.apache.ignite.tests.pojos.Person; -import org.apache.ignite.tests.pojos.PersonId; -import org.apache.ignite.tests.pojos.Product; -import org.apache.ignite.tests.pojos.ProductOrder; +import org.apache.ignite.tests.pojos.*; import org.apache.ignite.tests.utils.CacheStoreHelper; import org.apache.ignite.tests.utils.CassandraHelper; import org.apache.ignite.tests.utils.TestCacheSession; @@ -432,6 +429,74 @@ public class CassandraDirectPersistenceTest { /** */ @Test @SuppressWarnings("unchecked") + public void pojoStrategySimpleObjectsTest() { + CacheStore store5 = CacheStoreHelper.createCacheStore("persons5", + new ClassPathResource("org/apache/ignite/tests/persistence/pojo/persistence-settings-5.xml"), + CassandraHelper.getAdminDataSrc()); + + CacheStore store6 = CacheStoreHelper.createCacheStore("persons6", + new ClassPathResource("org/apache/ignite/tests/persistence/pojo/persistence-settings-6.xml"), + CassandraHelper.getAdminDataSrc()); + + Collection> entries5 = TestsHelper.generateSimplePersonIdsPersonsEntries(); + Collection> entries6 = TestsHelper.generateSimplePersonIdsPersonsEntries(); + + LOGGER.info("Running POJO strategy write tests for simple objects"); + + LOGGER.info("Running single write operation tests"); + store5.write(entries5.iterator().next()); + store6.write(entries6.iterator().next()); + LOGGER.info("Single write operation tests passed"); + + LOGGER.info("Running bulk write operation tests"); + store5.writeAll(entries5); + store6.writeAll(entries6); + LOGGER.info("Bulk write operation tests passed"); + + LOGGER.info("POJO strategy write tests for simple objects passed"); + + LOGGER.info("Running POJO simple objects strategy read tests"); + + LOGGER.info("Running single read operation tests"); + + SimplePerson person = (SimplePerson)store5.load(entries5.iterator().next().getKey()); + if (!entries5.iterator().next().getValue().equalsPrimitiveFields(person)) + throw new RuntimeException("SimplePerson values were incorrectly deserialized from Cassandra"); + + person = (SimplePerson)store6.load(entries6.iterator().next().getKey()); + if (!entries6.iterator().next().getValue().equalsPrimitiveFields(person)) + throw new RuntimeException("SimplePerson values were incorrectly deserialized from Cassandra"); + + LOGGER.info("Single read operation tests passed"); + + LOGGER.info("Running bulk read operation tests"); + + Map persons = store5.loadAll(TestsHelper.getKeys(entries5)); + if (!TestsHelper.checkSimplePersonCollectionsEqual(persons, entries5, true)) + throw new RuntimeException("SimplePerson values were incorrectly deserialized from Cassandra"); + + persons = store6.loadAll(TestsHelper.getKeys(entries6)); + if (!TestsHelper.checkSimplePersonCollectionsEqual(persons, entries6, true)) + throw new RuntimeException("SimplePerson values were incorrectly deserialized from Cassandra"); + + LOGGER.info("Bulk read operation tests passed"); + + LOGGER.info("POJO strategy read tests for simple objects passed"); + + LOGGER.info("Running POJO strategy delete tests for simple objects"); + + store5.delete(entries5.iterator().next().getKey()); + store5.deleteAll(TestsHelper.getKeys(entries5)); + + store6.delete(entries6.iterator().next().getKey()); + store6.deleteAll(TestsHelper.getKeys(entries6)); + + LOGGER.info("POJO strategy delete tests for simple objects passed"); + } + + /** */ + @Test + @SuppressWarnings("unchecked") public void pojoStrategyTransactionTest() { Map sessionProps = U.newHashMap(1); Transaction sessionTx = new TestTransaction(); http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/java/org/apache/ignite/tests/DDLGeneratorTest.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/DDLGeneratorTest.java b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/DDLGeneratorTest.java index e982e16..048f0f7 100644 --- a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/DDLGeneratorTest.java +++ b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/DDLGeneratorTest.java @@ -28,8 +28,12 @@ public class DDLGeneratorTest { /** */ private static final String[] RESOURCES = new String[] { "org/apache/ignite/tests/persistence/primitive/persistence-settings-1.xml", + "org/apache/ignite/tests/persistence/pojo/persistence-settings-1.xml", + "org/apache/ignite/tests/persistence/pojo/persistence-settings-2.xml", "org/apache/ignite/tests/persistence/pojo/persistence-settings-3.xml", "org/apache/ignite/tests/persistence/pojo/persistence-settings-4.xml", + "org/apache/ignite/tests/persistence/pojo/persistence-settings-5.xml", + "org/apache/ignite/tests/persistence/pojo/persistence-settings-6.xml", "org/apache/ignite/tests/persistence/pojo/product.xml", "org/apache/ignite/tests/persistence/pojo/order.xml" }; http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java index feccb24..21f28e0 100644 --- a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java +++ b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/IgnitePersistentStoreTest.java @@ -32,10 +32,7 @@ import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.internal.binary.BinaryMarshaller; import org.apache.ignite.internal.processors.cache.CacheEntryImpl; import org.apache.ignite.internal.util.typedef.internal.U; -import org.apache.ignite.tests.pojos.Person; -import org.apache.ignite.tests.pojos.PersonId; -import org.apache.ignite.tests.pojos.Product; -import org.apache.ignite.tests.pojos.ProductOrder; +import org.apache.ignite.tests.pojos.*; import org.apache.ignite.tests.utils.CacheStoreHelper; import org.apache.ignite.tests.utils.CassandraHelper; import org.apache.ignite.tests.utils.TestsHelper; @@ -423,6 +420,86 @@ public class IgnitePersistentStoreTest { /** */ @Test + public void pojoStrategySimpleObjectsTest() { + Ignition.stopAll(true); + + LOGGER.info("Running POJO strategy write tests for simple objects"); + + Map personMap5 = TestsHelper.generateSimplePersonIdsPersonsMap(); + Map personMap6 = TestsHelper.generateSimplePersonIdsPersonsMap(); + + try (Ignite ignite = Ignition.start("org/apache/ignite/tests/persistence/pojo/ignite-config.xml")) { + IgniteCache personCache5 = ignite.getOrCreateCache(new CacheConfiguration("cache5")); + IgniteCache personCache6 = ignite.getOrCreateCache(new CacheConfiguration("cache6")); + + LOGGER.info("Running single operation write tests"); + + SimplePersonId id = TestsHelper.generateRandomSimplePersonId(); + personCache5.put(id, TestsHelper.generateRandomSimplePerson(id.personNum)); + personCache6.put(id, TestsHelper.generateRandomSimplePerson(id.personNum)); + + LOGGER.info("Single operation write tests passed"); + + LOGGER.info("Running bulk operation write tests"); + personCache5.putAll(personMap5); + personCache6.putAll(personMap6); + LOGGER.info("Bulk operation write tests passed"); + } + + LOGGER.info("POJO strategy write tests for simple objects passed"); + + Ignition.stopAll(true); + + try (Ignite ignite = Ignition.start("org/apache/ignite/tests/persistence/pojo/ignite-config.xml")) { + LOGGER.info("Running POJO strategy read tests for simple objects"); + + IgniteCache personCache5 = ignite.getOrCreateCache(new CacheConfiguration("cache5")); + IgniteCache personCache6 = ignite.getOrCreateCache(new CacheConfiguration("cache6")); + + LOGGER.info("Running single operation read tests"); + + SimplePersonId id = personMap5.keySet().iterator().next(); + + SimplePerson person = personCache5.get(id); + if (!person.equalsPrimitiveFields(personMap5.get(id))) + throw new RuntimeException("SimplePerson value was incorrectly deserialized from Cassandra"); + + id = personMap6.keySet().iterator().next(); + + person = personCache6.get(id); + if (!person.equals(personMap6.get(id))) + throw new RuntimeException("SimplePerson value was incorrectly deserialized from Cassandra"); + + LOGGER.info("Single operation read tests passed"); + + LOGGER.info("Running bulk operation read tests"); + + Map persons5 = personCache5.getAll(personMap5.keySet()); + if (!TestsHelper.checkSimplePersonMapsEqual(persons5, personMap5, true)) + throw new RuntimeException("SimplePerson values batch was incorrectly deserialized from Cassandra"); + + Map persons6 = personCache6.getAll(personMap6.keySet()); + if (!TestsHelper.checkSimplePersonMapsEqual(persons6, personMap6, false)) + throw new RuntimeException("SimplePerson values batch was incorrectly deserialized from Cassandra"); + + LOGGER.info("Bulk operation read tests passed"); + + LOGGER.info("POJO strategy read tests for simple objects passed"); + + LOGGER.info("Running POJO strategy delete tests for simple objects"); + + personCache5.remove(id); + personCache5.removeAll(personMap5.keySet()); + + personCache6.remove(id); + personCache6.removeAll(personMap6.keySet()); + + LOGGER.info("POJO strategy delete tests for simple objects passed"); + } + } + + /** */ + @Test public void pojoStrategyTransactionTest() { CassandraHelper.dropTestKeyspaces(); http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/java/org/apache/ignite/tests/pojos/SimplePerson.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/pojos/SimplePerson.java b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/pojos/SimplePerson.java new file mode 100644 index 0000000..0d24ea6 --- /dev/null +++ b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/pojos/SimplePerson.java @@ -0,0 +1,186 @@ +/* + * 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.ignite.tests.pojos; + +import org.apache.ignite.cache.query.annotations.QuerySqlField; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.Date; +import java.util.List; + +/** + * Simple POJO without getters/setters which could be stored as a value in Ignite cache + */ +public class SimplePerson implements Externalizable { + /** */ + @QuerySqlField(name = "person_num") + private long personNum; + + /** */ + @QuerySqlField(name = "first_name") + private String firstName; + + /** */ + @QuerySqlField(name = "last_name") + private String lastName; + + /** */ + @QuerySqlField(name = "age") + private int age; + + /** */ + @QuerySqlField(name = "married", index = true) + private boolean married; + + /** */ + @QuerySqlField(name = "height") + private long height; + + /** */ + @QuerySqlField(name = "weight") + private float weight; + + /** */ + @QuerySqlField(name = "birth_date") + private Date birthDate; + + /** */ + @QuerySqlField(name = "phones") + private List phones; + + /** */ + @SuppressWarnings("UnusedDeclaration") + public SimplePerson() { + } + + /** */ + public SimplePerson(Person person) { + this.personNum = person.getPersonNumber(); + this.firstName = person.getFirstName(); + this.lastName = person.getLastName(); + this.age = person.getAge(); + this.married = person.getMarried(); + this.height = person.getHeight(); + this.weight = person.getWeight(); + this.birthDate = person.getBirthDate(); + this.phones = person.getPhones(); + } + + /** */ + public SimplePerson(long personNum, String firstName, String lastName, int age, boolean married, + long height, float weight, Date birthDate, List phones) { + this.personNum = personNum; + this.firstName = firstName; + this.lastName = lastName; + this.age = age; + this.married = married; + this.height = height; + this.weight = weight; + this.birthDate = birthDate; + this.phones = phones; + } + + + /** {@inheritDoc} */ + @Override public void writeExternal(ObjectOutput out) throws IOException { + out.writeLong(personNum); + out.writeObject(firstName); + out.writeObject(lastName); + out.writeInt(age); + out.writeBoolean(married); + out.writeLong(height); + out.writeFloat(weight); + out.writeObject(birthDate); + out.writeObject(phones); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + personNum = in.readLong(); + firstName = (String)in.readObject(); + lastName = (String)in.readObject(); + age = in.readInt(); + married = in.readBoolean(); + height = in.readLong(); + weight = in.readFloat(); + birthDate = (Date)in.readObject(); + phones = (List)in.readObject(); + } + + /** {@inheritDoc} */ + @SuppressWarnings("SimplifiableIfStatement") + @Override public boolean equals(Object obj) { + if (obj == null || !(obj instanceof SimplePerson)) + return false; + + SimplePerson person = (SimplePerson)obj; + + if (personNum != person.personNum) + return false; + + if ((firstName != null && !firstName.equals(person.firstName)) || + (person.firstName != null && !person.firstName.equals(firstName))) + return false; + + if ((lastName != null && !lastName.equals(person.lastName)) || + (person.lastName != null && !person.lastName.equals(lastName))) + return false; + + if ((birthDate != null && !birthDate.equals(person.birthDate)) || + (person.birthDate != null && !person.birthDate.equals(birthDate))) + return false; + + if ((phones != null && !phones.equals(person.phones)) || + (person.phones != null && !person.phones.equals(phones))) + return false; + + return age == person.age && married == person.married && + height == person.height && weight == person.weight; + } + + /** */ + @SuppressWarnings("SimplifiableIfStatement") + public boolean equalsPrimitiveFields(Object obj) { + if (obj == null || !(obj instanceof SimplePerson)) + return false; + + SimplePerson person = (SimplePerson)obj; + + if (personNum != person.personNum) + return false; + + if ((firstName != null && !firstName.equals(person.firstName)) || + (person.firstName != null && !person.firstName.equals(firstName))) + return false; + + if ((lastName != null && !lastName.equals(person.lastName)) || + (person.lastName != null && !person.lastName.equals(lastName))) + return false; + + if ((birthDate != null && !birthDate.equals(person.birthDate)) || + (person.birthDate != null && !person.birthDate.equals(birthDate))) + return false; + + return age == person.age && married == person.married && + height == person.height && weight == person.weight; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/java/org/apache/ignite/tests/pojos/SimplePersonId.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/pojos/SimplePersonId.java b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/pojos/SimplePersonId.java new file mode 100644 index 0000000..8dfbb2c --- /dev/null +++ b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/pojos/SimplePersonId.java @@ -0,0 +1,89 @@ +/* + * 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.ignite.tests.pojos; + +import org.apache.ignite.cache.affinity.AffinityKeyMapped; +import org.apache.ignite.cache.query.annotations.QuerySqlField; + +import java.io.Serializable; + +/** + * Simple POJO without getters/setters which could be stored as a key in Ignite cache + */ +public class SimplePersonId implements Serializable { + /** */ + @AffinityKeyMapped + @QuerySqlField(name = "company_code") + public String companyCode; + + /** */ + @AffinityKeyMapped + @QuerySqlField(name = "department_code") + public String departmentCode; + + /** */ + @QuerySqlField(name = "person_num") + public long personNum; + + /** */ + @SuppressWarnings("UnusedDeclaration") + public SimplePersonId() { + } + + /** */ + public SimplePersonId(PersonId personId) { + this.companyCode = personId.getCompanyCode(); + this.departmentCode = personId.getDepartmentCode(); + this.personNum = personId.getPersonNumber(); + } + + /** */ + public SimplePersonId(String companyCode, String departmentCode, long personNum) { + this.companyCode = companyCode; + this.departmentCode = departmentCode; + this.personNum = personNum; + } + + /** {@inheritDoc} */ + @SuppressWarnings("SimplifiableIfStatement") + @Override public boolean equals(Object obj) { + if (obj == null || !(obj instanceof SimplePersonId)) + return false; + + SimplePersonId id = (SimplePersonId)obj; + + if ((companyCode != null && !companyCode.equals(id.companyCode)) || + (id.companyCode != null && !id.companyCode.equals(companyCode))) + return false; + + if ((companyCode != null && !companyCode.equals(id.companyCode)) || + (id.companyCode != null && !id.companyCode.equals(companyCode))) + return false; + + return personNum == id.personNum; + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + String code = (companyCode == null ? "" : companyCode) + + (departmentCode == null ? "" : departmentCode) + + personNum; + + return code.hashCode(); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestsHelper.java ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestsHelper.java b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestsHelper.java index 24d64c9..5d21613 100644 --- a/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestsHelper.java +++ b/modules/cassandra/store/src/test/java/org/apache/ignite/tests/utils/TestsHelper.java @@ -21,10 +21,7 @@ package org.apache.ignite.tests.utils; import org.apache.ignite.cache.store.cassandra.common.SystemHelper; import org.apache.ignite.internal.processors.cache.CacheEntryImpl; import org.apache.ignite.tests.load.Generator; -import org.apache.ignite.tests.pojos.Person; -import org.apache.ignite.tests.pojos.PersonId; -import org.apache.ignite.tests.pojos.Product; -import org.apache.ignite.tests.pojos.ProductOrder; +import org.apache.ignite.tests.pojos.*; import org.springframework.core.io.ClassPathResource; import java.util.Collection; @@ -283,6 +280,24 @@ public class TestsHelper { } /** */ + public static Map generateSimplePersonIdsPersonsMap() { + return generateSimplePersonIdsPersonsMap(BULK_OPERATION_SIZE); + } + + /** */ + public static Map generateSimplePersonIdsPersonsMap(int cnt) { + Map map = new HashMap<>(); + + for (int i = 0; i < cnt; i++) { + PersonId id = generateRandomPersonId(); + + map.put(new SimplePersonId(id), new SimplePerson(generateRandomPerson(id.getPersonNumber()))); + } + + return map; + } + + /** */ public static Map generatePersonIdsPersonsMap() { return generatePersonIdsPersonsMap(BULK_OPERATION_SIZE); } @@ -301,6 +316,24 @@ public class TestsHelper { } /** */ + public static Collection> generateSimplePersonIdsPersonsEntries() { + return generateSimplePersonIdsPersonsEntries(BULK_OPERATION_SIZE); + } + + /** */ + public static Collection> generateSimplePersonIdsPersonsEntries(int cnt) { + Collection> entries = new LinkedList<>(); + + for (int i = 0; i < cnt; i++) { + PersonId id = generateRandomPersonId(); + + entries.add(new CacheEntryImpl<>(new SimplePersonId(id), new SimplePerson(generateRandomPerson(id.getPersonNumber())))); + } + + return entries; + } + + /** */ public static Collection> generatePersonIdsPersonsEntries() { return generatePersonIdsPersonsEntries(BULK_OPERATION_SIZE); } @@ -443,6 +476,24 @@ public class TestsHelper { } /** */ + public static SimplePerson generateRandomSimplePerson(long personNum) { + int phonesCnt = RANDOM.nextInt(4); + + List phones = new LinkedList<>(); + + for (int i = 0; i < phonesCnt; i++) + phones.add(randomNumber(4)); + + return new SimplePerson(personNum, randomString(4), randomString(4), RANDOM.nextInt(100), + RANDOM.nextBoolean(), RANDOM.nextLong(), RANDOM.nextFloat(), new Date(), phones); + } + + /** */ + public static SimplePersonId generateRandomSimplePersonId() { + return new SimplePersonId(randomString(4), randomString(4), RANDOM.nextInt(100)); + } + + /** */ public static Person generateRandomPerson(long personNum) { int phonesCnt = RANDOM.nextInt(4); @@ -517,6 +568,26 @@ public class TestsHelper { } /** */ + public static boolean checkSimplePersonMapsEqual(Map map1, Map map2, + boolean primitiveFieldsOnly) { + if (map1 == null || map2 == null || map1.size() != map2.size()) + return false; + + for (K key : map1.keySet()) { + SimplePerson person1 = map1.get(key); + SimplePerson person2 = map2.get(key); + + boolean equals = person1 != null && person2 != null && + (primitiveFieldsOnly ? person1.equalsPrimitiveFields(person2) : person1.equals(person2)); + + if (!equals) + return false; + } + + return true; + } + + /** */ public static boolean checkPersonMapsEqual(Map map1, Map map2, boolean primitiveFieldsOnly) { if (map1 == null || map2 == null || map1.size() != map2.size()) @@ -537,6 +608,24 @@ public class TestsHelper { } /** */ + public static boolean checkSimplePersonCollectionsEqual(Map map, Collection> col, + boolean primitiveFieldsOnly) { + if (map == null || col == null || map.size() != col.size()) + return false; + + for (CacheEntryImpl entry : col) { + boolean equals = primitiveFieldsOnly ? + entry.getValue().equalsPrimitiveFields(map.get(entry.getKey())) : + entry.getValue().equals(map.get(entry.getKey())); + + if (!equals) + return false; + } + + return true; + } + + /** */ public static boolean checkPersonCollectionsEqual(Map map, Collection> col, boolean primitiveFieldsOnly) { if (map == null || col == null || map.size() != col.size()) http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/ignite-config.xml ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/ignite-config.xml b/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/ignite-config.xml index af4ffef..75041fd 100644 --- a/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/ignite-config.xml +++ b/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/ignite-config.xml @@ -46,6 +46,16 @@ + + + + + + + + + + @@ -112,6 +122,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/persistence-settings-5.xml ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/persistence-settings-5.xml b/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/persistence-settings-5.xml new file mode 100644 index 0000000..f4210b8 --- /dev/null +++ b/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/persistence-settings-5.xml @@ -0,0 +1,21 @@ + + + + + + http://git-wip-us.apache.org/repos/asf/ignite/blob/d44fdd45/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/persistence-settings-6.xml ---------------------------------------------------------------------- diff --git a/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/persistence-settings-6.xml b/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/persistence-settings-6.xml new file mode 100644 index 0000000..340f646 --- /dev/null +++ b/modules/cassandra/store/src/test/resources/org/apache/ignite/tests/persistence/pojo/persistence-settings-6.xml @@ -0,0 +1,174 @@ + + + + + + + REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor' : 3} + AND DURABLE_WRITES = true + + + + + comment = 'A most excellent and useful table' + AND read_repair_chance = 0.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +