001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.directory.shared.ldap.model.message;
021
022
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.Map;
026
027import org.apache.directory.shared.ldap.model.exception.MessageException;
028
029
030/**
031 * Abstract message base class.
032 * 
033 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
034 */
035public abstract class AbstractMessage implements Message
036{
037    private static final long serialVersionUID = 7601738291101182094L;
038
039    /** Map of message controls using OID Strings for keys and Control values */
040    protected final Map<String, Control> controls;
041
042    /** The session unique message sequence identifier */
043    private int id;
044
045    /** The message type enumeration */
046    private final MessageTypeEnum type;
047
048    /** Transient Message Parameter Hash */
049    private final Map<Object, Object> parameters;
050
051
052    /**
053     * Completes the instantiation of a Message.
054     * 
055     * @param id the seq id of the message
056     * @param type the type of the message
057     */
058    protected AbstractMessage( final int id, final MessageTypeEnum type )
059    {
060        this.id = id;
061        this.type = type;
062        controls = new HashMap<String, Control>();
063        parameters = new HashMap<Object, Object>();
064    }
065
066
067    /**
068     * Gets the session unique message sequence id for this message. Requests
069     * and their responses if any have the same message id. Clients at the
070     * initialization of a session start with the first message's id set to 1
071     * and increment it with each transaction.
072     * 
073     * @return the session unique message id.
074     */
075    public int getMessageId()
076    {
077        return id;
078    }
079
080
081    /**
082     * {@inheritDoc}
083     */
084    public Message setMessageId( int id )
085    {
086        this.id = id;
087        
088        return this;
089    }
090
091
092    /**
093     * {@inheritDoc}
094     */
095    public Map<String, Control> getControls()
096    {
097        return Collections.unmodifiableMap( controls );
098    }
099
100
101    /**
102     * {@inheritDoc}
103     */
104    public Control getControl( String oid )
105    {
106        return controls.get( oid );
107    }
108
109
110    /**
111     * {@inheritDoc}
112     */
113    public boolean hasControl( String oid )
114    {
115        return controls.containsKey( oid );
116    }
117
118
119    /**
120     * {@inheritDoc}
121     */
122    public Message addControl( Control control ) throws MessageException
123    {
124        controls.put( control.getOid(), control );
125        
126        return this;
127    }
128
129
130    /**
131     * Deletes a control removing it from this Message.
132     * 
133     * @param control the control to remove.
134     * @throws MessageException if controls cannot be added to this Message or the control is
135     *             not known etc.
136     */
137    public Message removeControl( Control control ) throws MessageException
138    {
139        controls.remove( control.getOid() );
140        
141        return this;
142    }
143
144
145    /**
146     * Gets the LDAP message type code associated with this Message. Each
147     * request and response type has a unique message type code defined by the
148     * protocol in <a href="http://www.faqs.org/rfcs/rfc2251.html">RFC 2251</a>.
149     * 
150     * @return the message type code.
151     */
152    public MessageTypeEnum getType()
153    {
154        return type;
155    }
156
157
158    /**
159     * Gets a message scope parameter. Message scope parameters are temporary
160     * variables associated with a message and are set locally to be used to
161     * associate housekeeping information with a request or its processing.
162     * These parameters are never transmitted nor received, think of them as
163     * transient data associated with the message or its processing. These
164     * transient parameters are not locked down so modifications can occur
165     * without firing LockExceptions even when this Lockable is in the locked
166     * state.
167     * 
168     * @param key the key used to access a message parameter.
169     * @return the transient message parameter value.
170     */
171    public Object get( Object key )
172    {
173        return parameters.get( key );
174    }
175
176
177    /**
178     * Sets a message scope parameter. These transient parameters are not locked
179     * down so modifications can occur without firing LockExceptions even when
180     * this Lockable is in the locked state.
181     * 
182     * @param key the parameter key
183     * @param value the parameter value
184     * @return the old value or null
185     */
186    public Object put( Object key, Object value )
187    {
188        return parameters.put( key, value );
189    }
190
191
192    /**
193     * Checks to see if two messages are equivalent. Messages equivalence does
194     * not factor in parameters accessible through the get() and put()
195     * operations, nor do they factor in the Lockable properties of the Message.
196     * Only the type, controls, and the messageId are evaluated for equality.
197     * 
198     * @param obj the object to compare this Message to for equality
199     */
200    public boolean equals( Object obj )
201    {
202        if ( obj == this )
203        {
204            return true;
205        }
206
207        if ( ( obj == null ) || !( obj instanceof Message ) )
208        {
209            return false;
210        }
211
212        Message msg = ( Message ) obj;
213
214        if ( msg.getMessageId() != id )
215        {
216            return false;
217        }
218
219        if ( msg.getType() != type )
220        {
221            return false;
222        }
223
224        Map<String, Control> controls = msg.getControls();
225
226        if ( controls.size() != this.controls.size() )
227        {
228            return false;
229        }
230
231        for ( String key : this.controls.keySet() )
232        {
233            if ( ! controls.containsKey( key ) )
234            {
235                return false;
236            }
237        }
238
239        return true;
240    }
241
242
243    /**
244     * @see Object#hashCode()
245     * @return the instance's hash code 
246     */
247    public int hashCode()
248    {
249        int hash = 37;
250        hash = hash * 17 + id;
251        hash = hash * 17 + ( type == null ? 0 : type.hashCode() );
252        hash = hash * 17 + ( parameters == null ? 0 : parameters.hashCode() );
253        hash = hash * 17 + ( controls == null ? 0 : controls.hashCode() );
254
255        return hash;
256    }
257
258
259    /**
260     * {@inheritDoc}
261     */
262    public Message addAllControls( Control[] controls ) throws MessageException
263    {
264        for ( Control c : controls )
265        {
266            this.controls.put( c.getOid(), c );
267        }
268        
269        return this;
270    }
271
272
273    /**
274     * Get a String representation of a LdapMessage
275     * 
276     * @return A LdapMessage String
277     */
278    public String toString( String message )
279    {
280        StringBuilder sb = new StringBuilder();
281        
282        sb.append( "MessageType : " ).append( type ).append( '\n' );
283        sb.append( "Message ID : " ).append( id ).append( '\n' );
284        
285        sb.append( message );
286
287        if ( controls != null )
288        {
289            for ( Control control : controls.values() )
290            {
291                sb.append( control );
292            }
293        }
294
295        return sb.toString();
296    }
297}