001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.replication.plugin; 028 029 import java.util.ArrayList; 030 import java.util.Iterator; 031 import java.util.LinkedHashSet; 032 033 import org.opends.server.replication.common.ChangeNumber; 034 import org.opends.server.types.Attribute; 035 import org.opends.server.types.AttributeValue; 036 import org.opends.server.types.Entry; 037 import org.opends.server.types.Modification; 038 import org.opends.server.types.ModificationType; 039 040 /** 041 * This classes is used to store historical information for single valued 042 * attributes. 043 * One object of this type is created for each attribute that was changed in 044 * the entry. 045 * It allows to record the last time a given value was added, 046 * and the last time the whole attribute was deleted. 047 */ 048 public class AttrInfoSingle extends AttributeInfo 049 { 050 private ChangeNumber deleteTime = null; // last time when the attribute was 051 // deleted 052 private ChangeNumber addTime = null; // last time when a value was added 053 private AttributeValue value = null; // last added value 054 055 /** 056 * {@inheritDoc} 057 */ 058 @Override 059 public ChangeNumber getDeleteTime() 060 { 061 return deleteTime; 062 } 063 064 /** 065 * {@inheritDoc} 066 */ 067 @Override 068 public ArrayList<ValueInfo> getValuesInfo() 069 { 070 if (addTime == null) 071 { 072 return new ArrayList<ValueInfo>(); 073 } 074 else 075 { 076 ArrayList<ValueInfo> values = new ArrayList<ValueInfo>(); 077 values.add(new ValueInfo(value, addTime, null)); 078 return values; 079 } 080 } 081 082 /** 083 * {@inheritDoc} 084 */ 085 @Override 086 public void processLocalOrNonConflictModification(ChangeNumber changeNumber, 087 Modification mod) 088 { 089 Attribute modAttr = mod.getAttribute(); 090 LinkedHashSet<AttributeValue> values = null; 091 if (modAttr != null) 092 values = modAttr.getValues(); 093 AttributeValue newValue = null; 094 if (values.size() != 0) 095 newValue = values.iterator().next(); 096 097 switch (mod.getModificationType()) 098 { 099 case DELETE: 100 deleteTime = changeNumber; 101 value = newValue; 102 break; 103 104 case ADD: 105 addTime = changeNumber; 106 value = newValue; 107 break; 108 109 case REPLACE: 110 if (newValue == null) 111 { 112 // REPLACE with null value is actually a DELETE 113 deleteTime = changeNumber; 114 } 115 else 116 { 117 deleteTime = addTime = changeNumber; 118 } 119 value = newValue; 120 break; 121 122 case INCREMENT: 123 /* FIXME : we should update ChangeNumber */ 124 break; 125 } 126 } 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override 132 public boolean replayOperation(Iterator<Modification> modsIterator, 133 ChangeNumber changeNumber, Entry modifiedEntry, Modification mod) 134 { 135 boolean conflict = false; 136 137 Attribute modAttr = mod.getAttribute(); 138 LinkedHashSet<AttributeValue> values = null; 139 if (modAttr != null) 140 values = modAttr.getValues(); 141 AttributeValue newValue = null; 142 if (values.size() != 0) 143 newValue = values.iterator().next(); 144 145 switch (mod.getModificationType()) 146 { 147 case DELETE: 148 if ((changeNumber.newer(addTime)) && 149 ((newValue == null) || ((newValue != null) 150 && (newValue.equals(value))))) 151 { 152 deleteTime = changeNumber; 153 } 154 else 155 { 156 conflict = true; 157 modsIterator.remove(); 158 } 159 break; 160 161 case ADD: 162 if (changeNumber.newerOrEquals(deleteTime) && changeNumber.older(addTime)) 163 { 164 conflict = true; 165 mod.setModificationType(ModificationType.REPLACE); 166 addTime = changeNumber; 167 value = newValue; 168 } 169 else 170 { 171 if (changeNumber.newerOrEquals(deleteTime) 172 && ((addTime == null ) || addTime.older(deleteTime))) 173 { 174 // no conflict : don't do anything beside setting the addTime 175 addTime = changeNumber; 176 value = newValue; 177 } 178 else 179 { 180 conflict = true; 181 modsIterator.remove(); 182 } 183 } 184 185 break; 186 187 case REPLACE: 188 if ((changeNumber.older(deleteTime)) && (changeNumber.older(deleteTime))) 189 { 190 conflict = true; 191 modsIterator.remove(); 192 } 193 else 194 { 195 if (newValue == null) 196 { 197 value = newValue; 198 deleteTime = changeNumber; 199 } 200 else 201 { 202 addTime = changeNumber; 203 value = newValue; 204 deleteTime = changeNumber; 205 } 206 } 207 break; 208 209 case INCREMENT: 210 /* FIXME : we should update ChangeNumber */ 211 break; 212 } 213 return conflict; 214 } 215 216 /** 217 * {@inheritDoc} 218 */ 219 @Override 220 public void load(HistKey histKey, AttributeValue value, ChangeNumber cn) 221 { 222 switch (histKey) 223 { 224 case ADD: 225 addTime = cn; 226 this.value = value; 227 break; 228 229 case DEL: 230 deleteTime = cn; 231 if (value != null) 232 this.value = value; 233 break; 234 235 case REPL: 236 addTime = deleteTime = cn; 237 if (value != null) 238 this.value = value; 239 break; 240 241 case DELATTR: 242 deleteTime = cn; 243 break; 244 } 245 } 246 } 247