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<>();
059        parameters = new HashMap<>();
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    @Override
072    public int getMessageId()
073    {
074        return id;
075    }
076
077
078    /**
079     * {@inheritDoc}
080     */
081    @Override
082    public Message setMessageId( int id )
083    {
084        this.id = id;
085
086        return this;
087    }
088
089
090    /**
091     * {@inheritDoc}
092     */
093    @Override
094    public Map<String, Control> getControls()
095    {
096        return Collections.unmodifiableMap( controls );
097    }
098
099
100    /**
101     * {@inheritDoc}
102     */
103    @Override
104    public Control getControl( String oid )
105    {
106        return controls.get( oid );
107    }
108
109
110    /**
111     * {@inheritDoc}
112     */
113    @Override
114    public boolean hasControl( String oid )
115    {
116        return controls.containsKey( oid );
117    }
118
119
120    /**
121     * {@inheritDoc}
122     */
123    @Override
124    public Message addControl( Control control )
125    {
126        controls.put( control.getOid(), control );
127
128        return this;
129    }
130
131
132    /**
133     * Deletes a control removing it from this Message.
134     * 
135     * @param control the control to remove.
136     */
137    @Override
138    public Message removeControl( Control control )
139    {
140        controls.remove( control.getOid() );
141
142        return this;
143    }
144
145
146    /**
147     * Gets the LDAP message type code associated with this Message. Each
148     * request and response type has a unique message type code defined by the
149     * protocol in <a href="http://www.faqs.org/rfcs/rfc2251.html">RFC 2251</a>.
150     * 
151     * @return the message type code.
152     */
153    @Override
154    public MessageTypeEnum getType()
155    {
156        return type;
157    }
158
159
160    /**
161     * Gets a message scope parameter. Message scope parameters are temporary
162     * variables associated with a message and are set locally to be used to
163     * associate housekeeping information with a request or its processing.
164     * These parameters are never transmitted nor received, think of them as
165     * transient data associated with the message or its processing. These
166     * transient parameters are not locked down so modifications can occur
167     * without firing LockExceptions even when this Lockable is in the locked
168     * state.
169     * 
170     * @param key the key used to access a message parameter.
171     * @return the transient message parameter value.
172     */
173    @Override
174    public Object get( Object key )
175    {
176        return parameters.get( key );
177    }
178
179
180    /**
181     * Sets a message scope parameter. These transient parameters are not locked
182     * down so modifications can occur without firing LockExceptions even when
183     * this Lockable is in the locked state.
184     * 
185     * @param key the parameter key
186     * @param value the parameter value
187     * @return the old value or null
188     */
189    @Override
190    public Object put( Object key, Object value )
191    {
192        return parameters.put( key, value );
193    }
194
195
196    /**
197     * Checks to see if two messages are equivalent. Messages equivalence does
198     * not factor in parameters accessible through the get() and put()
199     * operations, nor do they factor in the Lockable properties of the Message.
200     * Only the type, controls, and the messageId are evaluated for equality.
201     * 
202     * @param obj the object to compare this Message to for equality
203     */
204    @Override
205    public boolean equals( Object obj )
206    {
207        if ( obj == this )
208        {
209            return true;
210        }
211
212        if ( ( obj == null ) || !( obj instanceof Message ) )
213        {
214            return false;
215        }
216
217        Message msg = ( Message ) obj;
218
219        if ( msg.getMessageId() != id )
220        {
221            return false;
222        }
223
224        if ( msg.getType() != type )
225        {
226            return false;
227        }
228
229        Map<String, Control> controlMap = msg.getControls();
230
231        if ( controlMap.size() != controls.size() )
232        {
233            return false;
234        }
235
236        for ( String key : controls.keySet() )
237        {
238            if ( !controlMap.containsKey( key ) )
239            {
240                return false;
241            }
242        }
243
244        return true;
245    }
246
247
248    /**
249     * @see Object#hashCode()
250     * @return the instance's hash code 
251     */
252    @Override
253    public int hashCode()
254    {
255        int hash = 37;
256        hash = hash * 17 + id;
257        hash = hash * 17 + ( type == null ? 0 : type.hashCode() );
258        hash = hash * 17 + ( parameters == null ? 0 : parameters.hashCode() );
259        hash = hash * 17 + ( controls == null ? 0 : controls.hashCode() );
260
261        return hash;
262    }
263
264
265    /**
266     * {@inheritDoc}
267     */
268    @Override
269    public Message addAllControls( Control[] controls )
270    {
271        for ( Control c : controls )
272        {
273            this.controls.put( c.getOid(), c );
274        }
275
276        return this;
277    }
278
279
280    /**
281     * Get a String representation of a LdapMessage
282     * 
283     * @param message The message to print
284     * @return A LdapMessage String
285     */
286    public String toString( String message )
287    {
288        StringBuilder sb = new StringBuilder();
289
290        sb.append( "MessageType : " ).append( type ).append( '\n' );
291        sb.append( "Message ID : " ).append( id ).append( '\n' );
292
293        sb.append( message );
294
295        if ( controls != null )
296        {
297            for ( Control control : controls.values() )
298            {
299                sb.append( control );
300            }
301        }
302
303        return sb.toString();
304    }
305}