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 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.replication.protocol;
028    
029    import static org.opends.server.replication.protocol.OperationContext.*;
030    
031    import java.io.UnsupportedEncodingException;
032    import java.util.zip.DataFormatException;
033    
034    import org.opends.server.core.ModifyDNOperationBasis;
035    import org.opends.server.protocols.asn1.ASN1OctetString;
036    import org.opends.server.protocols.internal.InternalClientConnection;
037    import org.opends.server.replication.common.ChangeNumber;
038    import org.opends.server.types.AbstractOperation;
039    import org.opends.server.types.DN;
040    import org.opends.server.types.DirectoryException;
041    import org.opends.server.types.RDN;
042    import org.opends.server.types.operation.PostOperationModifyDNOperation;
043    
044    /**
045     * Message used to send Modify DN information.
046     */
047    public class ModifyDNMsg extends UpdateMessage
048    {
049      private String newRDN;
050      private String newSuperior;
051      private boolean deleteOldRdn;
052      private String newSuperiorId;
053      private static final long serialVersionUID = -4905520652801395185L;
054    
055      /**
056       * construct a new Modify DN message.
057       *
058       * @param operation The operation to use for building the message
059       */
060      public ModifyDNMsg(PostOperationModifyDNOperation operation)
061      {
062        super((OperationContext) operation.getAttachment(SYNCHROCONTEXT),
063            operation.getRawEntryDN().stringValue());
064    
065        ModifyDnContext ctx =
066          (ModifyDnContext) operation.getAttachment(SYNCHROCONTEXT);
067        newSuperiorId = ctx.getNewParentId();
068    
069        deleteOldRdn = operation.deleteOldRDN();
070        if (operation.getRawNewSuperior() != null)
071          newSuperior = operation.getRawNewSuperior().stringValue();
072        else
073          newSuperior = null;
074        newRDN = operation.getRawNewRDN().stringValue();
075      }
076    
077      /**
078       * construct a new Modify DN message.
079       *
080       * @param dn The dn to use for building the message.
081       * @param changeNumber The changeNumberto use for building the message.
082       * @param uid The unique id to use for building the message.
083       * @param newParentUid The new parent unique id to use for building
084       *                     the message.
085       * @param deleteOldRdn boolean indicating if old rdn must be deleted to use
086       *                     for building the message.
087       * @param newSuperior The new Superior entry to use for building the message.
088       * @param newRDN The new Rdn to use for building the message.
089       */
090      public ModifyDNMsg(String dn, ChangeNumber changeNumber, String uid,
091                         String newParentUid, boolean deleteOldRdn,
092                         String newSuperior, String newRDN)
093      {
094        super(new ModifyDnContext(changeNumber, uid, newParentUid), dn);
095    
096        newSuperiorId = newParentUid;
097    
098        this.deleteOldRdn = deleteOldRdn;
099        this.newSuperior = newSuperior;
100        this.newRDN = newRDN;
101      }
102    
103      /**
104       * Creates a new ModifyDN message from a byte[].
105       *
106       * @param in The byte[] from which the operation must be read.
107       * @throws DataFormatException The input byte[] is not a valid AddMsg.
108       * @throws UnsupportedEncodingException If UTF8 is not supported.
109       */
110      public ModifyDNMsg(byte[] in) throws DataFormatException,
111                                           UnsupportedEncodingException
112      {
113        super(in);
114    
115        int pos = decodeHeader(MSG_TYPE_MODIFYDN_REQUEST, in);
116    
117        /* read the newRDN
118         * first calculate the length then construct the string
119         */
120        int length = getNextLength(in, pos);
121        newRDN = new String(in, pos, length, "UTF-8");
122        pos += length + 1;
123    
124        /* read the newSuperior
125         * first calculate the length then construct the string
126         */
127        length = getNextLength(in, pos);
128        if (length != 0)
129          newSuperior = new String(in, pos, length, "UTF-8");
130        else
131          newSuperior = null;
132        pos += length + 1;
133    
134        /* read the new parent Id
135         */
136        length = getNextLength(in, pos);
137        if (length != 0)
138          newSuperiorId = new String(in, pos, length, "UTF-8");
139        else
140          newSuperiorId = null;
141        pos += length + 1;
142    
143        /* get the deleteoldrdn flag */
144        if (in[pos] == 0)
145          deleteOldRdn = false;
146        else
147          deleteOldRdn = true;
148      }
149    
150      /**
151       * {@inheritDoc}
152       */
153      @Override
154      public AbstractOperation createOperation(
155             InternalClientConnection connection, String newDn)
156      {
157        ModifyDNOperationBasis moddn =  new ModifyDNOperationBasis(connection,
158                   InternalClientConnection.nextOperationID(),
159                   InternalClientConnection.nextMessageID(), null,
160                   new ASN1OctetString(newDn), new ASN1OctetString(newRDN),
161                   deleteOldRdn,
162                   (newSuperior == null ? null : new ASN1OctetString(newSuperior)));
163        ModifyDnContext ctx = new ModifyDnContext(getChangeNumber(), getUniqueId(),
164                                                  newSuperiorId);
165        moddn.setAttachment(SYNCHROCONTEXT, ctx);
166        return moddn;
167      }
168    
169      /**
170       * Get the byte array representation of this Message.
171       *
172       * @return The byte array representation of this Message.
173       *
174       * @throws UnsupportedEncodingException  When the encoding of the message
175       *         failed because the UTF-8 encoding is not supported.
176       */
177      @Override
178      public byte[] getBytes() throws UnsupportedEncodingException
179      {
180        byte[] byteNewRdn = newRDN.getBytes("UTF-8");
181        byte[] byteNewSuperior = null;
182        byte[] byteNewSuperiorId = null;
183    
184        // calculate the length necessary to encode the parameters
185        int length = byteNewRdn.length + 1 + 1;
186        if (newSuperior != null)
187        {
188          byteNewSuperior = newSuperior.getBytes("UTF-8");
189          length += byteNewSuperior.length + 1;
190        }
191        else
192          length += 1;
193    
194        if (newSuperiorId != null)
195        {
196          byteNewSuperiorId = newSuperiorId.getBytes("UTF-8");
197          length += byteNewSuperiorId.length + 1;
198        }
199        else
200          length += 1;
201    
202        byte[] resultByteArray = encodeHeader(MSG_TYPE_MODIFYDN_REQUEST, length);
203        int pos = resultByteArray.length - length;
204    
205        /* put the new RDN and a terminating 0 */
206        pos = addByteArray(byteNewRdn, resultByteArray, pos);
207    
208        /* put the newsuperior and a terminating 0 */
209        if (newSuperior != null)
210        {
211          pos = addByteArray(byteNewSuperior, resultByteArray, pos);
212        }
213        else
214          resultByteArray[pos++] = 0;
215    
216        /* put the newsuperiorId and a terminating 0 */
217        if (newSuperiorId != null)
218        {
219          pos = addByteArray(byteNewSuperiorId, resultByteArray, pos);
220        }
221        else
222          resultByteArray[pos++] = 0;
223    
224        /* put the deleteoldrdn flag */
225        if (deleteOldRdn)
226          resultByteArray[pos++] = 1;
227        else
228          resultByteArray[pos++] = 0;
229    
230        return resultByteArray;
231      }
232    
233      /**
234       * {@inheritDoc}
235       */
236      @Override
237      public String toString()
238      {
239        return ("MODDN " + getDn() + " " + newRDN + " " + newSuperior + " " +
240                getChangeNumber());
241      }
242    
243      /**
244       * Set the new superior.
245       * @param string the new superior.
246       */
247      public void setNewSuperior(String string)
248      {
249        newSuperior = string;
250      }
251    
252      /**
253       * Get the new RDN of this operation.
254       *
255       * @return The new RDN of this operation.
256       */
257      public String getNewRDN()
258      {
259        return newRDN;
260      }
261    
262      /**
263       * Set the new RDN of this operation.
264       * @param newRDN the new RDN of this operation.
265       */
266      public void setNewRDN(String newRDN)
267      {
268        this.newRDN = newRDN;
269      }
270    
271      /**
272       * Check if this MSG will change the DN of the target entry to be
273       * the same as the dn given as a parameter.
274       * @param targetDn the DN to use when checking if this MSG will change
275       *                 the DN of the entry to a given DN.
276       * @return A boolean indicating if the modify DN MSG will change the DN of
277       *         the target entry to be the same as the dn given as a parameter.
278       */
279      public boolean newDNIsParent(DN targetDn)
280      {
281        try
282        {
283          DN newDN;
284          if (newSuperior == null)
285          {
286            DN parentDn = DN.decode(this.getDn()).getParent();
287            newDN = parentDn.concat(RDN.decode(newRDN));
288          }
289          else
290          {
291            String newStringDN = newRDN + "," + newSuperior;
292            newDN = DN.decode(newStringDN);
293          }
294    
295    
296          if (newDN.isAncestorOf(targetDn))
297            return true;
298          else
299            return false;
300        } catch (DirectoryException e)
301        {
302          // The DN was not a correct DN, and therefore does not a parent of the
303          // DN given as a parameter.
304          return false;
305        }
306      }
307    
308      /**
309       * Check if the new dn of this ModifyDNMsg is the same as the targetDN
310       * given in parameter.
311       *
312       * @param targetDN The targetDN to use to check for equality.
313       *
314       * @return A boolean indicating if the targetDN if the same as the new DN of
315       *         the ModifyDNMsg.
316       */
317      public boolean newDNIsEqual(DN targetDN)
318      {
319        try
320        {
321          String newStringDN = newRDN + "," + newSuperior;
322          DN newDN = DN.decode(newStringDN);
323    
324          if (newDN.equals(targetDN))
325            return true;
326          else
327            return false;
328        } catch (DirectoryException e)
329        {
330          // The DN was not a correct DN, and therefore does not match the
331          // DN given as a parameter.
332          return false;
333        }
334      }
335    
336      /**
337       * Check if the new parent of the modifyDNMsg is the same as the targetDN
338       * given in parameter.
339       *
340       * @param targetDN the targetDN to use when checking equality.
341       *
342       * @return A boolean indicating if the new parent of the modifyDNMsg is the
343       *         same as the targetDN.
344       */
345      public boolean newParentIsEqual(DN targetDN)
346      {
347        try
348        {
349          DN newSuperiorDN = DN.decode(newSuperior);
350    
351          if (newSuperiorDN.equals(targetDN))
352            return true;
353          else
354            return false;
355        } catch (DirectoryException e)
356        {
357          // The newsuperior was not a correct DN, and therefore does not match the
358          // DN given as a parameter.
359          return false;
360        }
361      }
362    
363    }