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