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