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