OPENDJ-3281 Modify operations may not be replayed if case is mixed on attribute values
Regression introduced in OPENDJ-1368, when AttributeValue class was removed
and replaced by plain ByteString.
Historical values are wrongly compared for equality by using ByteString.equals(),
while they should take use the attribute type's equality matching rule for determining equality.
AttrValueHistorical.java:
Added attributeType field used in equals() and hashCode().
AttrHistoricalMultipleTest.java:
Fixed the unit test that reproduces the bug.
ValueInfoTest.java: RENAMED to AttrValueHistoricalTest.java
1 files renamed
7 files modified
| | |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | |
| | |
| | | /** |
| | | * Create a new object from a provided attribute type. Historical is empty. |
| | | * |
| | | * @param type the provided attribute type. |
| | | * @param attrType the provided attribute type. |
| | | * @return a new AttributeInfo object. |
| | | */ |
| | | public static AttrHistorical createAttributeHistorical(AttributeType type) |
| | | public static AttrHistorical createAttributeHistorical(AttributeType attrType) |
| | | { |
| | | return type.isSingleValue() ? new AttrHistoricalSingle() : new AttrHistoricalMultiple(); |
| | | return attrType.isSingleValue() ? new AttrHistoricalSingle(attrType) : new AttrHistoricalMultiple(); |
| | | } |
| | | |
| | | /** |
| | |
| | | * Assign the provided information to this object. |
| | | * |
| | | * @param histKey the key to assign. |
| | | * @param attrType the associated attribute type. |
| | | * @param value the associated value or null if there is no value; |
| | | * @param csn the associated CSN. |
| | | */ |
| | | public abstract void assign(HistAttrModificationKey histKey, ByteString value, CSN csn); |
| | | public abstract void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn); |
| | | } |
| | |
| | | */ |
| | | void delete(Attribute attr, CSN csn) |
| | | { |
| | | AttributeType attrType = attr.getAttributeDescription().getAttributeType(); |
| | | for (ByteString val : attr) |
| | | { |
| | | delete(val, csn); |
| | | delete(val, attrType, csn); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Update the historical of this attribute after a delete value. |
| | | * |
| | | * @param val value that was deleted |
| | | * @param csn time when the delete was done |
| | | */ |
| | | void delete(ByteString val, CSN csn) |
| | | * Update the historical of this attribute after a delete value. |
| | | * |
| | | * @param val |
| | | * value that was deleted |
| | | * @param attrType |
| | | * @param csn |
| | | * time when the delete was done |
| | | */ |
| | | void delete(ByteString val, AttributeType attrType, CSN csn) |
| | | { |
| | | update(csn, new AttrValueHistorical(val, null, csn)); |
| | | update(csn, new AttrValueHistorical(val, attrType, null, csn)); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | private void add(Attribute attr, CSN csn) |
| | | { |
| | | AttributeType attrType = attr.getAttributeDescription().getAttributeType(); |
| | | for (ByteString val : attr) |
| | | { |
| | | add(val, csn); |
| | | add(val, attrType, csn); |
| | | } |
| | | } |
| | | |
| | |
| | | * |
| | | * @param addedValue |
| | | * values that was added |
| | | * @param attrType |
| | | * @param csn |
| | | * time when the value was added |
| | | */ |
| | | void add(ByteString addedValue, CSN csn) |
| | | void add(ByteString addedValue, AttributeType attrType, CSN csn) |
| | | { |
| | | update(csn, new AttrValueHistorical(addedValue, csn, null)); |
| | | update(csn, new AttrValueHistorical(addedValue, attrType, csn, null)); |
| | | } |
| | | |
| | | private void update(CSN csn, AttrValueHistorical valInfo) |
| | |
| | | // we are processing DELETE of some attribute values |
| | | AttributeBuilder builder = new AttributeBuilder(modAttr); |
| | | |
| | | AttributeType attrType = modAttr.getAttributeDescription().getAttributeType(); |
| | | for (ByteString val : modAttr) |
| | | { |
| | | boolean deleteIt = true; // true if the delete must be done |
| | | boolean addedInCurrentOp = false; |
| | | |
| | | // update historical information |
| | | AttrValueHistorical valInfo = new AttrValueHistorical(val, null, csn); |
| | | AttrValueHistorical valInfo = new AttrValueHistorical(val, attrType, null, csn); |
| | | AttrValueHistorical oldValInfo = valuesHist.get(valInfo); |
| | | if (oldValInfo != null) |
| | | { |
| | |
| | | return false; |
| | | } |
| | | |
| | | AttributeBuilder builder = new AttributeBuilder(m.getAttribute()); |
| | | for (ByteString addVal : m.getAttribute()) |
| | | Attribute attribute = m.getAttribute(); |
| | | AttributeBuilder builder = new AttributeBuilder(attribute); |
| | | AttributeType attrType = attribute.getAttributeDescription().getAttributeType(); |
| | | for (ByteString addVal : attribute) |
| | | { |
| | | AttrValueHistorical valInfo = new AttrValueHistorical(addVal, csn, null); |
| | | AttrValueHistorical valInfo = new AttrValueHistorical(addVal, attrType, csn, null); |
| | | AttrValueHistorical oldValInfo = valuesHist.get(valInfo); |
| | | if (oldValInfo == null) |
| | | { |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void assign(HistAttrModificationKey histKey, ByteString value, CSN csn) |
| | | public void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn) |
| | | { |
| | | switch (histKey) |
| | | { |
| | | case ADD: |
| | | if (value != null) |
| | | { |
| | | add(value, csn); |
| | | add(value, attrType, csn); |
| | | } |
| | | break; |
| | | |
| | | case DEL: |
| | | if (value != null) |
| | | { |
| | | delete(value, csn); |
| | | delete(value, attrType, csn); |
| | | } |
| | | break; |
| | | |
| | |
| | | delete(csn); |
| | | if (value != null) |
| | | { |
| | | add(value, csn); |
| | | add(value, attrType, csn); |
| | | } |
| | | break; |
| | | |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.types.Attribute; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | |
| | |
| | | */ |
| | | public class AttrHistoricalSingle extends AttrHistorical |
| | | { |
| | | /** Last time when the attribute was deleted. */ |
| | | private CSN deleteTime; |
| | | /** Last time when a value was added. */ |
| | | private CSN addTime; |
| | | /** Last added value. */ |
| | | private ByteString value; |
| | | /** Attribute type for this historical value */ |
| | | private AttributeType attributeType; |
| | | /** Last time when a value was added. */ |
| | | private CSN addTime; |
| | | /** Last time when the attribute was deleted. */ |
| | | private CSN deleteTime; |
| | | /** |
| | | * Last operation applied. This is only used for multiple mods on the same |
| | | * single valued attribute in the same modification. |
| | | */ |
| | | private HistAttrModificationKey lastMod; |
| | | |
| | | /** |
| | | * Builds an {@link AttrHistoricalSingle} object. |
| | | * |
| | | * @param attributeType |
| | | * the attribute type for this historical value |
| | | */ |
| | | public AttrHistoricalSingle(AttributeType attributeType) |
| | | { |
| | | this.attributeType = attributeType; |
| | | } |
| | | |
| | | @Override |
| | | public CSN getDeleteTime() |
| | | { |
| | |
| | | { |
| | | if (addTime != null) |
| | | { |
| | | return Collections.singleton(new AttrValueHistorical(value, addTime, null)); |
| | | return Collections.singleton(new AttrValueHistorical(value, attributeType, addTime, null)); |
| | | } |
| | | return Collections.emptySet(); |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void assign(HistAttrModificationKey histKey, ByteString value, CSN csn) |
| | | public void assign(HistAttrModificationKey histKey, AttributeType attrType, ByteString value, CSN csn) |
| | | { |
| | | switch (histKey) |
| | | { |
| | |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2015 ForgeRock AS. |
| | | * Portions Copyright 2013-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.forgerock.util.Reject.*; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.opends.server.replication.common.CSN; |
| | | |
| | | /** AttrValueHistorical is the historical information of the modification of one attribute value. */ |
| | | public class AttrValueHistorical |
| | | { |
| | | private AttributeType attributeType; |
| | | private ByteString value; |
| | | private ByteString normalizedValue; |
| | | private CSN valueDeleteTime; |
| | | private CSN valueUpdateTime; |
| | | |
| | | /** |
| | | * Build an AttrValueHistorical for a provided attribute value, providing |
| | | * the last time the provided value is either updated or deleted. |
| | | * @param value the provided attributeValue |
| | | * @param value the provided attribute value |
| | | * @param attributeType the provided attribute type |
| | | * @param csnUpdate last time when this value was updated |
| | | * @param csnDelete last time when this value for deleted |
| | | */ |
| | | public AttrValueHistorical(ByteString value, CSN csnUpdate, CSN csnDelete) |
| | | public AttrValueHistorical(ByteString value, AttributeType attributeType, CSN csnUpdate, CSN csnDelete) |
| | | { |
| | | this.value = value; |
| | | this.attributeType = checkNotNull(attributeType); |
| | | this.valueUpdateTime = csnUpdate; |
| | | this.valueDeleteTime = csnDelete; |
| | | } |
| | |
| | | if (obj instanceof AttrValueHistorical) |
| | | { |
| | | AttrValueHistorical objVal = (AttrValueHistorical) obj; |
| | | return value.equals(objVal.getAttributeValue()); |
| | | try |
| | | { |
| | | return getNormalizedValue().equals(objVal.getNormalizedValue()); |
| | | } |
| | | catch (DecodeException e) |
| | | { |
| | | return value.equals(objVal.getAttributeValue()); |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | |
| | | @Override |
| | | public int hashCode() |
| | | { |
| | | return value.hashCode(); |
| | | try |
| | | { |
| | | return getNormalizedValue().hashCode(); |
| | | } |
| | | catch (DecodeException e) |
| | | { |
| | | return value.hashCode(); |
| | | } |
| | | } |
| | | |
| | | private ByteString getNormalizedValue() throws DecodeException |
| | | { |
| | | if (normalizedValue == null) |
| | | { |
| | | normalizedValue = attributeType.getEqualityMatchingRule().normalizeAttributeValue(value); |
| | | } |
| | | return normalizedValue; |
| | | } |
| | | |
| | | /** |
| | |
| | | attrInfo = AttrHistorical.createAttributeHistorical(attrDesc.getAttributeType()); |
| | | newHistorical.attributesHistorical.put(attrDesc, attrInfo); |
| | | } |
| | | attrInfo.assign(histVal.getHistKey(), histVal.getAttributeValue(), csn); |
| | | attrInfo.assign(histVal.getHistKey(), attrDesc.getAttributeType(), histVal.getAttributeValue(), csn); |
| | | } |
| | | } |
| | | } |
| | |
| | | import java.util.Collections; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.CoreSchema; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | |
| | | |
| | | /** Create a AttrInfo and check the methods. */ |
| | | @Test(dataProvider = "attrInfo") |
| | | public void attrInfo(ByteString att, CSN deleteTime, CSN updateTime) throws Exception |
| | | public void attrInfo(ByteString attrValue, CSN deleteTime, CSN updateTime) throws Exception |
| | | { |
| | | AttributeType attrType = CoreSchema.getDescriptionAttributeType(); |
| | | // Create an empty AttrInfo |
| | | AttrHistoricalMultiple attrInfo1 = new AttrHistoricalMultiple(); |
| | | |
| | | // Check |
| | | attrInfo1.add(att, updateTime); |
| | | Set<AttrValueHistorical> values1 = attrInfo1.getValuesHistorical(); |
| | | assertEquals(values1.size(), 1); |
| | | AttrValueHistorical valueInfo1 = new AttrValueHistorical(att, updateTime, null); |
| | | assertTrue(values1.contains(valueInfo1)); |
| | | attrInfo1.add(attrValue, attrType, updateTime); |
| | | assertThat(attrInfo1.getValuesHistorical()) |
| | | .containsOnly(new AttrValueHistorical(attrValue, attrType, updateTime, null)); |
| | | |
| | | // Check constructor with parameter |
| | | AttrValueHistorical valueInfo2 = new AttrValueHistorical(att, updateTime, deleteTime); |
| | | AttrValueHistorical valueInfo2 = new AttrValueHistorical(attrValue, attrType, updateTime, deleteTime); |
| | | AttrHistoricalMultiple attrInfo2 = new AttrHistoricalMultiple( |
| | | deleteTime, updateTime, Collections.singletonMap(valueInfo2, valueInfo2)); |
| | | deleteTime, updateTime, Collections.singleton(valueInfo2)); |
| | | |
| | | // Check equality |
| | | //assertTrue(attrInfo1.getDeleteTime().compareTo(attrInfo2.getDeleteTime())==0); |
| | | |
| | | // Check constructor with time parameter and not Value |
| | | AttrHistoricalMultiple attrInfo3 = new AttrHistoricalMultiple(deleteTime, updateTime, null); |
| | | attrInfo3.add(att, updateTime); |
| | | Set<AttrValueHistorical> values3 = attrInfo3.getValuesHistorical(); |
| | | assertEquals(values3.size(), 1); |
| | | valueInfo1 = new AttrValueHistorical(att, updateTime, null); |
| | | assertTrue(values3.contains(valueInfo1)); |
| | | attrInfo3.add(attrValue, attrType, updateTime); |
| | | assertThat(attrInfo3.getValuesHistorical()) |
| | | .containsOnly(new AttrValueHistorical(attrValue, attrType, updateTime, null)); |
| | | |
| | | // Check duplicate |
| | | AttrHistoricalMultiple attrInfo4 = attrInfo3.duplicate(); |
| | | Set<AttrValueHistorical> values4 = attrInfo4.getValuesHistorical(); |
| | | AttrHistoricalMultiple attrInfo4 = duplicate(attrInfo3); |
| | | assertEquals(attrInfo4.getDeleteTime().compareTo(attrInfo3.getDeleteTime()), 0); |
| | | assertEquals(values4.size(), values3.size()); |
| | | assertThat(attrInfo4.getValuesHistorical()).isEqualTo(attrInfo3.getValuesHistorical()); |
| | | |
| | | // Check |
| | | attrInfo4.delete(att, updateTime); |
| | | attrInfo4.delete(attrValue, attrType, updateTime); |
| | | assertEquals(attrInfo4.getValuesHistorical().size(), 1); |
| | | |
| | | // Check |
| | | AttributeType type = DirectoryServer.getSchema().getAttributeType(ATTRIBUTE_NAME); |
| | | attrInfo3.delete(Attributes.create(type, att), updateTime) ; |
| | | attrInfo3.delete(Attributes.create(type, attrValue), updateTime); |
| | | assertEquals(attrInfo3.getValuesHistorical().size(), 1); |
| | | |
| | | // Check |
| | | attrInfo2.delete(updateTime); |
| | | assertEquals(attrInfo2.getValuesHistorical().size(), 0); |
| | | assertThat(attrInfo2.getValuesHistorical()).isEmpty(); |
| | | } |
| | | |
| | | private AttrHistoricalMultiple duplicate(AttrHistoricalMultiple attrMul) |
| | | { |
| | | return new AttrHistoricalMultiple( |
| | | attrMul.getDeleteTime(), attrMul.getLastUpdateTime(), attrMul.getValuesHistorical()); |
| | | } |
| | | |
| | | @Test |
| | |
| | | assertAttributeValues(entry, "X"); |
| | | } |
| | | |
| | | /** Use case: user changes the case of a family name for example. */ |
| | | @Test |
| | | public void replay_addThenDeleteThenAdd_differentCaseWithCaseIgnoreAttributeType() throws Exception |
| | | { |
| | | CSN[] t = newCSNs(3); |
| | | |
| | | mod = newModification(ADD, "X"); |
| | | mod = newModification(ADD, "x"); |
| | | replayOperation(t[0], entry, mod, SUCCESS); |
| | | assertAttributeValues(entry, "X"); |
| | | assertAttributeValues(entry, "x"); |
| | | |
| | | mod = newModification(DELETE, "x"); |
| | | mod = newModification(DELETE, "X"); |
| | | replayOperation(t[1], entry, mod, SUCCESS); |
| | | assertNoAttributeValue(entry); |
| | | |
| | | mod = newModification(ADD, "X"); |
| | | replayOperationSuppressMod(t[2], entry, mod, CONFLICT); |
| | | assertAttributeValues(entry); |
| | | replayOperation(t[2], entry, mod, SUCCESS); |
| | | assertAttributeValues(entry, "X"); |
| | | } |
| | | |
| | | @Test |
| | |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.CSN; |
| | |
| | | @BeforeMethod |
| | | public void localSetUp() throws Exception |
| | | { |
| | | attrHist = new AttrHistoricalSingle(); |
| | | AttributeType attrType = Schema.getDefaultSchema().getAttributeType(ATTRIBUTE_NAME); |
| | | attrHist = new AttrHistoricalSingle(attrType); |
| | | csn = csnGen.newCSN(); |
| | | entry = TestCaseUtils.makeEntry( |
| | | "dn: uid=test.user", |
| | |
| | | @Test |
| | | public void replay_deleteDubious() throws Exception |
| | | { |
| | | HistoricalAttributeValue histAttrVal = new HistoricalAttributeValue("display:" + csn + ":add:X"); |
| | | attrHist.assign(histAttrVal.getHistKey(), histAttrVal.getAttributeValue(), csn); |
| | | AttributeType attrType = Schema.getDefaultSchema().getAttributeType(ATTRIBUTE_NAME); |
| | | HistoricalAttributeValue histAttrVal = new HistoricalAttributeValue(ATTRIBUTE_NAME + ":" + csn + ":add:X"); |
| | | attrHist.assign(histAttrVal.getHistKey(), attrType, histAttrVal.getAttributeValue(), csn); |
| | | mod = newModification(ADD, "X"); |
| | | entry.applyModification(mod); |
| | | assertAttributeValue(entry, "X"); |
| File was renamed from opendj-server-legacy/src/test/java/org/opends/server/replication/plugin/ValueInfoTest.java |
| | |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2013-2015 ForgeRock AS. |
| | | * Portions Copyright 2013-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import static org.assertj.core.api.Assertions.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.opendj.ldap.schema.CoreSchema; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.opends.server.util.TimeThread; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | /** |
| | | * Test of ValueInfo. |
| | | */ |
| | | /** Test of AttrValueHistorical. */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ValueInfoTest extends ReplicationTestCase |
| | | public class AttrValueHistoricalTest extends ReplicationTestCase |
| | | { |
| | | |
| | | /** |
| | | * Build some data for the ValueInfo test below. |
| | | */ |
| | | /** Build some data for the ValueInfo test below. */ |
| | | @DataProvider(name = "valueInfo") |
| | | public Object[][] createData() { |
| | | ByteString att1 = ByteString.valueOfUtf8("string"); |
| | |
| | | {att3,upd3,del3}}; |
| | | } |
| | | |
| | | /** |
| | | * Create a ValueInfo and check the methods. |
| | | */ |
| | | /** Create a ValueInfo and check the methods. */ |
| | | @Test(dataProvider = "valueInfo") |
| | | public void valueInfo(ByteString value, CSN csnUpdate, CSN csnDelete) throws Exception |
| | | { |
| | | AttrValueHistorical valInfo1 = new AttrValueHistorical(value, csnUpdate, csnDelete); |
| | | AttrValueHistorical valInfo2 = new AttrValueHistorical(value, csnUpdate, csnUpdate); |
| | | AttributeType attrType = CoreSchema.getDescriptionAttributeType(); |
| | | AttrValueHistorical valInfo1 = new AttrValueHistorical(value, attrType, csnUpdate, csnDelete); |
| | | AttrValueHistorical valInfo2 = new AttrValueHistorical(value, attrType, csnUpdate, csnUpdate); |
| | | AttrValueHistorical valInfo3 = new AttrValueHistorical(ByteString.valueOfUtf8("Test"), |
| | | csnUpdate, csnUpdate); |
| | | attrType, csnUpdate, csnUpdate); |
| | | |
| | | // Check equals |
| | | assertFalse(valInfo1.equals(new Object())); |
| | |
| | | assertEquals(valInfo1.getAttributeValue(), value); |
| | | assertEquals(valInfo1.isUpdate(), csnUpdate != null); |
| | | } |
| | | |
| | | @Test |
| | | public void testEqualsHashCode() |
| | | { |
| | | final AttrValueHistorical histVal1 = newAttrValueHistorical("description"); |
| | | final AttrValueHistorical histVal2 = newAttrValueHistorical("Description"); |
| | | final AttrValueHistorical histVal3 = newAttrValueHistorical("another description"); |
| | | |
| | | assertThat(histVal1).isEqualTo(histVal1); |
| | | assertThat(histVal1).isEqualTo(histVal2); |
| | | assertThat(histVal2).isEqualTo(histVal1); |
| | | |
| | | assertThat(histVal1).isNotEqualTo(histVal3); |
| | | |
| | | assertThat(histVal1.hashCode()).isEqualTo(histVal2.hashCode()); |
| | | assertThat(histVal1.hashCode()).isNotEqualTo(histVal3.hashCode()); |
| | | } |
| | | |
| | | private AttrValueHistorical newAttrValueHistorical(String pp) |
| | | { |
| | | AttributeType attrType = CoreSchema.getDescriptionAttributeType(); |
| | | return new AttrValueHistorical(ByteString.valueOfUtf8(pp), attrType, null, null); |
| | | } |
| | | } |