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 org.opends.server.core.AddOperationBasis;
030    import org.opends.server.core.DirectoryServer;
031    import org.opends.server.protocols.asn1.ASN1Element;
032    import org.opends.server.protocols.asn1.ASN1Exception;
033    import org.opends.server.protocols.asn1.ASN1OctetString;
034    
035    import java.io.UnsupportedEncodingException;
036    import java.util.ArrayList;
037    import java.util.Collection;
038    import java.util.LinkedHashSet;
039    import java.util.List;
040    import java.util.zip.DataFormatException;
041    
042    import org.opends.server.protocols.internal.InternalClientConnection;
043    import org.opends.server.protocols.ldap.LDAPAttribute;
044    import org.opends.server.replication.common.ChangeNumber;
045    import org.opends.server.types.AbstractOperation;
046    import org.opends.server.types.Attribute;
047    import org.opends.server.types.AttributeValue;
048    import org.opends.server.types.LDAPException;
049    import org.opends.server.types.RawAttribute;
050    import org.opends.server.types.operation.PostOperationAddOperation;
051    
052    import static org.opends.server.replication.protocol.OperationContext.*;
053    import static org.opends.server.util.StaticUtils.toLowerCase;
054    
055    /**
056     * This class is used to exchange Add operation between LDAP servers
057     * and replication servers.
058     */
059    public class AddMsg extends UpdateMessage
060    {
061      private static final long serialVersionUID = -4905520652801395185L;
062      private byte[] encodedAttributes;
063      private String parentUniqueId;
064    
065      /**
066       * Creates a new AddMessage.
067       * @param op the operation to use when creating the message
068       */
069      public AddMsg(PostOperationAddOperation op)
070      {
071        super((AddContext) op.getAttachment(SYNCHROCONTEXT),
072              op.getRawEntryDN().stringValue());
073    
074        AddContext ctx = (AddContext) op.getAttachment(SYNCHROCONTEXT);
075        this.parentUniqueId = ctx.getParentUid();
076    
077        // Encode the object classes (SET OF LDAPString).
078        LinkedHashSet<AttributeValue> ocValues =
079          new LinkedHashSet<AttributeValue>(op.getObjectClasses().size());
080        for (String s : op.getObjectClasses().values())
081        {
082          ocValues.add(new AttributeValue(new ASN1OctetString(s),
083                             new ASN1OctetString(toLowerCase(s))));
084        }
085    
086        Attribute attr = new Attribute(
087                     DirectoryServer.getObjectClassAttributeType(),
088                     "objectClass", ocValues);
089    
090        ArrayList<ASN1Element> elems = new ArrayList<ASN1Element>();
091    
092        elems.add(new LDAPAttribute(attr).encode());
093    
094        // Encode the user attributes (AttributeList).
095        for (List<Attribute> list : op.getUserAttributes().values())
096        {
097          for (Attribute a : list)
098          {
099            elems.add(new LDAPAttribute(a).encode());
100          }
101        }
102    
103        // Encode the operational attributes (AttributeList).
104        for (List<Attribute> list : op.getOperationalAttributes().values())
105        {
106          for (Attribute a : list)
107          {
108            elems.add(new LDAPAttribute(a).encode());
109          }
110        }
111    
112        // Encode the sequence.
113        encodedAttributes = ASN1Element.encodeValue(elems);
114      }
115    
116      /**
117       * Creates a new AddMessage.
118       *
119       * @param cn ChangeNumber of the add.
120       * @param dn DN of the added entry.
121       * @param uniqueId The Unique identifier of the added entry.
122       * @param parentId The unique Id of the parent of the added entry.
123       * @param objectClass objectclass of the added entry.
124       * @param userAttributes user attributes of the added entry.
125       * @param operationalAttributes operational attributes of the added entry.
126       */
127      public AddMsg(ChangeNumber cn,
128                    String dn,
129                    String uniqueId,
130                    String parentId,
131                    Attribute objectClass,
132                    Collection<Attribute> userAttributes,
133                    Collection<Attribute> operationalAttributes)
134      {
135        super (new AddContext(cn, uniqueId, parentId), dn);
136        this.parentUniqueId = parentId;
137    
138        ArrayList<ASN1Element> elems = new ArrayList<ASN1Element>();
139        elems.add(new LDAPAttribute(objectClass).encode());
140    
141        for (Attribute a : userAttributes)
142          elems.add(new LDAPAttribute(a).encode());
143    
144        if (operationalAttributes != null)
145          for (Attribute a : operationalAttributes)
146            elems.add(new LDAPAttribute(a).encode());
147    
148        encodedAttributes = ASN1Element.encodeValue(elems);
149      }
150    
151      /**
152       * Creates a new Add message from a byte[].
153       *
154       * @param in The byte[] from which the operation must be read.
155       * @throws DataFormatException The input byte[] is not a valid AddMsg
156       * @throws UnsupportedEncodingException If UTF8 is not supported by the jvm
157       */
158      public AddMsg(byte[] in) throws DataFormatException,
159                                      UnsupportedEncodingException
160      {
161        super(in);
162    
163        int  pos = decodeHeader(MSG_TYPE_ADD_REQUEST, in);
164    
165        // read the parent unique Id
166        int length = getNextLength(in, pos);
167        if (length != 0)
168        {
169          parentUniqueId = new String(in, pos, length, "UTF-8");
170          pos += length + 1;
171        }
172        else
173        {
174          parentUniqueId = null;
175          pos += 1;
176        }
177    
178        // Read the attributes : all the remaining bytes
179        encodedAttributes = new byte[in.length-pos];
180        int i =0;
181        while (pos<in.length)
182        {
183          encodedAttributes[i++] = in[pos++];
184        }
185      }
186    
187      /**
188       * {@inheritDoc}
189       */
190      @Override
191      public AbstractOperation createOperation(
192             InternalClientConnection connection, String newDn)
193             throws LDAPException, ASN1Exception
194      {
195        ArrayList<RawAttribute> attr = new ArrayList<RawAttribute>();
196        ArrayList<ASN1Element> elems;
197    
198        elems = ASN1Element.decodeElements(encodedAttributes);
199        for (ASN1Element elem : elems)
200        {
201          attr.add(LDAPAttribute.decode(elem));
202        }
203    
204        AddOperationBasis add =  new AddOperationBasis(connection,
205                                InternalClientConnection.nextOperationID(),
206                                InternalClientConnection.nextMessageID(), null,
207                                new ASN1OctetString(newDn), attr);
208        AddContext ctx = new AddContext(getChangeNumber(), getUniqueId(),
209                                        parentUniqueId);
210        add.setAttachment(SYNCHROCONTEXT, ctx);
211        return add;
212      }
213    
214      /**
215       * Get the byte[] representation of this Message.
216       *
217       * @return the byte array representation of this Message.
218       *
219       * @throws UnsupportedEncodingException When the encoding of the message
220       *         failed because the UTF-8 encoding is not supported.
221       */
222      @Override
223      public byte[] getBytes() throws UnsupportedEncodingException
224      {
225        int length = encodedAttributes.length;
226        byte[] byteParentId = null;
227        if (parentUniqueId != null)
228        {
229          byteParentId = parentUniqueId.getBytes("UTF-8");
230          length += byteParentId.length + 1;
231        }
232        else
233        {
234          length += 1;
235        }
236    
237        /* encode the header in a byte[] large enough to also contain the mods */
238        byte [] resultByteArray = encodeHeader(MSG_TYPE_ADD_REQUEST, length);
239    
240        int pos = resultByteArray.length - length;
241    
242        if (byteParentId != null)
243          pos = addByteArray(byteParentId, resultByteArray, pos);
244        else
245          resultByteArray[pos++] = 0;
246    
247        /* put the attributes */
248        for (int i=0; i<encodedAttributes.length; i++,pos++)
249        {
250          resultByteArray[pos] = encodedAttributes[i];
251        }
252        return resultByteArray;
253      }
254    
255      /**
256       * {@inheritDoc}
257       */
258      @Override
259      public String toString()
260      {
261        return ("ADD DN=(" + getDn() + ") CN=(" + getChangeNumber() + ")");
262      }
263    
264      /**
265       * Add the specified attribute/attribute value in the entry contained
266       * in this AddMsg.
267       *
268       * @param name  The name of the attribute to add.
269       * @param value The value of the attribute to add.
270       * @throws ASN1Exception When this Msg is not valid.
271       */
272      public void addAttribute(String name, String value)
273             throws ASN1Exception
274      {
275        RawAttribute newAttr = new LDAPAttribute(name, value);
276        ArrayList<ASN1Element> elems;
277        elems = ASN1Element.decodeElements(encodedAttributes);
278        elems.add(newAttr.encode());
279        encodedAttributes = ASN1Element.encodeValue(elems);
280      }
281    
282      /**
283       * Set the parent unique id of this add msg.
284       *
285       * @param uid the parent unique id.
286       */
287      public void setParentUid(String uid)
288      {
289        parentUniqueId = uid;
290      }
291    
292      /**
293       * Get the parent unique id of this add msg.
294       * @return the parent unique id.
295       */
296      public String getParentUid()
297      {
298        return parentUniqueId;
299      }
300    }