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;
021    
022    import java.util.Collections;
023    import java.util.HashMap;
024    import java.util.Iterator;
025    import java.util.Map;
026    
027    import javax.naming.ldap.Control;
028    
029    
030    
031    /**
032     * Abstract message base class.
033     * 
034     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
035     * @version $Rev: 764131 $
036     */
037    public abstract class InternalAbstractMessage implements InternalMessage
038    {
039        static final long serialVersionUID = 7601738291101182094L;
040    
041        /** Map of message controls using OID Strings for keys and Control values */
042        private final Map<String, Control> controls;
043    
044        /** The session unique message sequence identifier */
045        private final int id;
046    
047        /** The message type enumeration */
048        private final MessageTypeEnum type;
049    
050        /** Transient Message Parameter Hash */
051        private final Map<Object, Object> parameters;
052    
053    
054        /**
055         * Completes the instantiation of a Message.
056         * 
057         * @param id
058         *            the seq id of the message
059         * @param type
060         *            the type of the message
061         */
062        protected InternalAbstractMessage( final int id, final MessageTypeEnum type )
063        {
064            this.id = id;
065            this.type = type;
066            controls = new HashMap<String, Control>();
067            parameters = new HashMap<Object, Object>();
068        }
069    
070    
071        /**
072         * Gets the session unique message sequence id for this message. Requests
073         * and their responses if any have the same message id. Clients at the
074         * initialization of a session start with the first message's id set to 1
075         * and increment it with each transaction.
076         * 
077         * @return the session unique message id.
078         */
079        public int getMessageId()
080        {
081            return id;
082        }
083    
084    
085        /**
086         * Gets the controls associated with this message mapped by OID.
087         * 
088         * @return Map of OID strings to Control object instances.
089         * @see InternalControl
090         */
091        public Map<String, Control> getControls()
092        {
093            return Collections.unmodifiableMap( controls );
094        }
095    
096        
097        /**
098         * @see org.apache.directory.shared.ldap.message.InternalMessage#hasControl(java.lang.String)
099         */
100        public boolean hasControl( String oid )
101        {
102            return controls.containsKey( oid );
103        }
104        
105    
106        /**
107         * Adds a control to this Message.
108         * 
109         * @param control
110         *            the control to add.
111         * @throws MessageException
112         *             if controls cannot be added to this Message or the control is
113         *             not known etc.
114         */
115        public void add( Control control ) throws MessageException
116        {
117            controls.put( control.getID(), control );
118        }
119    
120    
121        /**
122         * Deletes a control removing it from this Message.
123         * 
124         * @param control
125         *            the control to remove.
126         * @throws MessageException
127         *             if controls cannot be added to this Message or the control is
128         *             not known etc.
129         */
130        public void remove( Control control ) throws MessageException
131        {
132            controls.remove( control.getID() );
133        }
134    
135    
136        /**
137         * Gets the LDAP message type code associated with this Message. Each
138         * request and response type has a unique message type code defined by the
139         * protocol in <a href="http://www.faqs.org/rfcs/rfc2251.html">RFC 2251</a>.
140         * 
141         * @return the message type code.
142         */
143        public MessageTypeEnum getType()
144        {
145            return type;
146        }
147    
148    
149        /**
150         * Gets a message scope parameter. Message scope parameters are temporary
151         * variables associated with a message and are set locally to be used to
152         * associate housekeeping information with a request or its processing.
153         * These parameters are never transmitted nor recieved, think of them as
154         * transient data associated with the message or its processing. These
155         * transient parameters are not locked down so modifications can occur
156         * without firing LockExceptions even when this Lockable is in the locked
157         * state.
158         * 
159         * @param key
160         *            the key used to access a message parameter.
161         * @return the transient message parameter value.
162         */
163        public Object get( Object key )
164        {
165            return parameters.get( key );
166        }
167    
168    
169        /**
170         * Sets a message scope parameter. These transient parameters are not locked
171         * down so modifications can occur without firing LockExceptions even when
172         * this Lockable is in the locked state.
173         * 
174         * @param key
175         *            the parameter key
176         * @param value
177         *            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
193         *            the object to compare this Message to for equality
194         */
195        public boolean equals( Object obj )
196        {
197            if ( obj == this )
198            {
199                return true;
200            }
201    
202            if ( ( obj == null ) || !( obj instanceof InternalMessage ) )
203            {
204                return false;
205            }
206    
207            InternalMessage msg = ( InternalMessage ) obj;
208    
209            if ( msg.getMessageId() != id )
210            {
211                return false;
212            }
213    
214            if ( msg.getType() != type )
215            {
216                return false;
217            }
218    
219            Map<String, Control> controls = msg.getControls();
220            
221            if ( controls.size() != this.controls.size() )
222            {
223                return false;
224            }
225    
226            Iterator<String> list = this.controls.keySet().iterator();
227            
228            while ( list.hasNext() )
229            {
230                if ( !controls.containsKey( list.next() ) )
231                {
232                    return false;
233                }
234            }
235    
236            return true;
237        }
238        
239        /**
240         * @see Object#hashCode()
241         * @return the instance's hash code 
242         */
243        public int hashCode()
244        {
245            int hash = 37;
246            hash = hash*17 + id;
247            hash = hash*17 + ( type == null ? 0 : type.hashCode() );
248            hash = hash*17 + ( parameters == null ? 0 : parameters.hashCode() );
249            hash = hash*17 + ( controls == null ? 0 : controls.hashCode() );
250            
251            return hash;
252        }
253    
254    
255        public void addAll( Control[] controls ) throws MessageException
256        {
257            for ( Control c : controls )
258            {
259                this.controls.put( c.getID(), c );
260            }
261        }
262    }