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     */
020    package org.apache.directory.shared.ldap.message.internal;
021    
022    import java.util.Collections;
023    import java.util.HashMap;
024    import java.util.Iterator;
025    import java.util.Map;
026    
027    import org.apache.directory.shared.ldap.codec.MessageTypeEnum;
028    import org.apache.directory.shared.ldap.codec.controls.CodecControl;
029    import org.apache.directory.shared.ldap.message.MessageException;
030    import org.apache.directory.shared.ldap.message.control.Control;
031    
032    
033    
034    /**
035     * Abstract message base class.
036     * 
037     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038     * @version $Rev: 910150 $
039     */
040    public abstract class InternalAbstractMessage implements InternalMessage
041    {
042        static final long serialVersionUID = 7601738291101182094L;
043    
044        /** Map of message controls using OID Strings for keys and Control values */
045        private final Map<String, Control> controls;
046    
047        /** The session unique message sequence identifier */
048        private final int id;
049    
050        /** The message type enumeration */
051        private final MessageTypeEnum type;
052    
053        /** Transient Message Parameter Hash */
054        private final Map<Object, Object> parameters;
055    
056    
057        /**
058         * Completes the instantiation of a Message.
059         * 
060         * @param id
061         *            the seq id of the message
062         * @param type
063         *            the type of the message
064         */
065        protected InternalAbstractMessage( final int id, final MessageTypeEnum type )
066        {
067            this.id = id;
068            this.type = type;
069            controls = new HashMap<String, Control>();
070            parameters = new HashMap<Object, Object>();
071        }
072    
073    
074        /**
075         * Gets the session unique message sequence id for this message. Requests
076         * and their responses if any have the same message id. Clients at the
077         * initialization of a session start with the first message's id set to 1
078         * and increment it with each transaction.
079         * 
080         * @return the session unique message id.
081         */
082        public int getMessageId()
083        {
084            return id;
085        }
086    
087    
088        /**
089         * Gets the controls associated with this message mapped by OID.
090         * 
091         * @return Map of OID strings to Control object instances.
092         * @see CodecControl
093         */
094        public Map<String, Control> getControls()
095        {
096            return Collections.unmodifiableMap( controls );
097        }
098    
099        
100        /**
101         * @see org.apache.directory.shared.ldap.message.internal.InternalMessage#hasControl(java.lang.String)
102         */
103        public boolean hasControl( String oid )
104        {
105            return controls.containsKey( oid );
106        }
107        
108    
109        /**
110         * Adds a control to this Message.
111         * 
112         * @param control
113         *            the control to add.
114         * @throws MessageException
115         *             if controls cannot be added to this Message or the control is
116         *             not known etc.
117         */
118        public void add( Control control ) throws MessageException
119        {
120            controls.put( control.getOid(), control );
121        }
122    
123    
124        /**
125         * Deletes a control removing it from this Message.
126         * 
127         * @param control
128         *            the control to remove.
129         * @throws MessageException
130         *             if controls cannot be added to this Message or the control is
131         *             not known etc.
132         */
133        public void remove( Control control ) throws MessageException
134        {
135            controls.remove( control.getOid() );
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 recieved, 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
163         *            the key used to access a message parameter.
164         * @return the transient message parameter value.
165         */
166        public Object get( Object key )
167        {
168            return parameters.get( key );
169        }
170    
171    
172        /**
173         * Sets a message scope parameter. These transient parameters are not locked
174         * down so modifications can occur without firing LockExceptions even when
175         * this Lockable is in the locked state.
176         * 
177         * @param key
178         *            the parameter key
179         * @param value
180         *            the parameter value
181         * @return the old value or null
182         */
183        public Object put( Object key, Object value )
184        {
185            return parameters.put( key, value );
186        }
187    
188    
189        /**
190         * Checks to see if two messages are equivalent. Messages equivalence does
191         * not factor in parameters accessible through the get() and put()
192         * operations, nor do they factor in the Lockable properties of the Message.
193         * Only the type, controls, and the messageId are evaluated for equality.
194         * 
195         * @param obj
196         *            the object to compare this Message to for equality
197         */
198        public boolean equals( Object obj )
199        {
200            if ( obj == this )
201            {
202                return true;
203            }
204    
205            if ( ( obj == null ) || !( obj instanceof InternalMessage ) )
206            {
207                return false;
208            }
209    
210            InternalMessage msg = ( InternalMessage ) obj;
211    
212            if ( msg.getMessageId() != id )
213            {
214                return false;
215            }
216    
217            if ( msg.getType() != type )
218            {
219                return false;
220            }
221    
222            Map<String, Control> controls = msg.getControls();
223            
224            if ( controls.size() != this.controls.size() )
225            {
226                return false;
227            }
228    
229            Iterator<String> list = this.controls.keySet().iterator();
230            
231            while ( list.hasNext() )
232            {
233                if ( !controls.containsKey( list.next() ) )
234                {
235                    return false;
236                }
237            }
238    
239            return true;
240        }
241        
242        /**
243         * @see Object#hashCode()
244         * @return the instance's hash code 
245         */
246        public int hashCode()
247        {
248            int hash = 37;
249            hash = hash*17 + id;
250            hash = hash*17 + ( type == null ? 0 : type.hashCode() );
251            hash = hash*17 + ( parameters == null ? 0 : parameters.hashCode() );
252            hash = hash*17 + ( controls == null ? 0 : controls.hashCode() );
253            
254            return hash;
255        }
256    
257    
258        public void addAll( Control[] controls ) throws MessageException
259        {
260            for ( Control c : controls )
261            {
262                this.controls.put( c.getOid(), c );
263            }
264        }
265    }