View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.mitosis.common;
21  
22  
23  import java.io.Serializable;
24  
25  
26  /**
27   * A default implementation of {@link CSN}.
28   * 
29   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
30   */
31  public class DefaultCSN implements CSN, Serializable, Comparable<CSN>
32  {
33      /**
34       * Declares the Serial Version Uid.
35       *
36       * @see <a
37       *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
38       *      Declare Serial Version Uid</a>
39       */
40      private static final long serialVersionUID = 1L;
41  
42      /** The timeStamp of this operation */
43      private final long timestamp;
44  
45      /** The server identification */
46      private final String replicaId;
47  
48      /** The operation number in the same timestamp */
49      private final int operationSequence;
50  
51      /** Stores the String representation of the CSN */
52      private transient String csnStr;
53  
54      /** Stores the byte array representation of the CSN */
55      private transient byte[] bytes;
56  
57  
58      /**
59       * Creates a new instance.
60       * 
61       * @param timestamp GMT timestamp of modification
62       * @param replicaId Replica ID where modification occurred (<tt>[-_A-Za-z0-9]{1,16}</tt>)
63       * @param operationSequence Operation sequence
64       */
65      public DefaultCSN( long timestamp, String replicaId, int operationSequence )
66      {
67          this.timestamp = timestamp;
68          this.replicaId = replicaId;
69          this.operationSequence = operationSequence;
70      }
71  
72  
73      /**
74       * Creates a new instance of SimpleCSN from a String.
75       * 
76       * The string format must be :
77       * &lt;timestamp> : &lt;replica ID> : &lt;operation sequence>
78       *
79       * @param value The String containing the CSN
80       */
81      public DefaultCSN( String value ) throws InvalidCSNException
82      {
83          assert value != null;
84  
85          int sepTS = value.indexOf( ':' );
86  
87          assert sepTS > 0;
88  
89          int sepID = value.lastIndexOf( ':' );
90  
91          if ( ( sepID == -1 ) || ( sepID == sepTS ) | ( sepID - sepTS < 2 ) )
92          {
93              throw new InvalidCSNException();
94          }
95  
96          try
97          {
98              timestamp = Long.parseLong( value.substring( 0, sepTS ), 16 );
99          }
100         catch ( NumberFormatException ife )
101         {
102             throw new InvalidCSNException();
103         }
104 
105         try
106         {
107             replicaId = value.substring( sepTS + 1, sepID );
108         }
109         catch ( IllegalArgumentException iae )
110         {
111             throw new InvalidCSNException();
112         }
113 
114         try
115         {
116             operationSequence = Integer.parseInt( value.substring( sepID + 1 ), 16 );
117         }
118         catch ( NumberFormatException ife )
119         {
120             throw new InvalidCSNException();
121         }
122     }
123 
124 
125     /**
126      * Creates a new instance of SimpleCSN from the serialized data
127      *
128      * @param value The byte array which contains the serialized CSN
129      */
130     public DefaultCSN( byte[] value )
131     {
132         timestamp = ( ( long ) ( value[0] & 0x00FF ) << 56 ) | ( ( long ) ( value[1] & 0x00FF ) << 48 )
133             | ( ( long ) ( value[2] & 0x00FF ) << 40 ) | ( ( long ) ( value[3] & 0x00FF ) << 32 )
134             | ( ( value[4] << 24 ) & 0x00000000FF000000L ) | ( ( value[5] << 16 ) & 0x0000000000FF0000L )
135             | ( ( value[6] << 8 ) & 0x000000000000FF00L ) | ( value[7] & 0x00000000000000FFL );
136 
137         operationSequence = ( ( value[8] & 0x00FF ) << 24 ) + ( ( value[9] & 0x00FF ) << 16 )
138             + ( ( value[10] & 0x00FF ) << 8 ) + ( value[11] & 0x00FF );
139 
140         char[] chars = new char[value.length - 12];
141 
142         for ( int i = 12; i < value.length; i++ )
143         {
144             chars[i - 12] = ( char ) ( value[i] & 0x00FF );
145         }
146 
147         replicaId = new String( chars );
148         bytes = value;
149     }
150 
151 
152     /**
153      * Return the CSN as a formated string. The used format is :
154      * &lt;timestamp> ':' &lt;replicaId> ':' &lt;operation sequence>
155      * 
156      * @return The CSN as a String
157      */
158     public String toOctetString()
159     {
160         if ( csnStr == null )
161         {
162             StringBuilder buf = new StringBuilder( 40 );
163             buf.append( timestamp );
164             buf.append( ':' );
165             buf.append( replicaId );
166             buf.append( ':' );
167             buf.append( operationSequence );
168             csnStr = buf.toString();
169         }
170 
171         return csnStr;
172     }
173 
174 
175     /**
176      * Get the CSN as a byte array. The data are stored as :
177      * bytes 1 to 8  : timestamp, big-endian
178      * bytes 9 to 12 : operation sequence, big-endian
179      * bytes 13 to ... : ReplicaId 
180      * 
181      * @return A byte array representing theCSN
182      */
183     public byte[] toBytes()
184     {
185         if ( bytes == null )
186         {
187             String id = replicaId;
188             byte[] bb = new byte[8 + id.length() + 4];
189 
190             bb[0] = ( byte ) ( timestamp >> 56 );
191             bb[1] = ( byte ) ( timestamp >> 48 );
192             bb[2] = ( byte ) ( timestamp >> 40 );
193             bb[3] = ( byte ) ( timestamp >> 32 );
194             bb[4] = ( byte ) ( timestamp >> 24 );
195             bb[5] = ( byte ) ( timestamp >> 16 );
196             bb[6] = ( byte ) ( timestamp >> 8 );
197             bb[7] = ( byte ) timestamp;
198             bb[8] = ( byte ) ( ( operationSequence >> 24 ) );
199             bb[9] = ( byte ) ( ( operationSequence >> 16 ) );
200             bb[10] = ( byte ) ( ( operationSequence >> 8 ) );
201             bb[11] = ( byte ) ( operationSequence );
202 
203             for ( int i = 0; i < id.length(); i++ )
204             {
205                 bb[12 + i] = ( byte ) id.charAt( i );
206             }
207 
208             bytes = bb;
209         }
210 
211         return bytes;
212     }
213 
214 
215     /**
216      * @return The timestamp
217      */
218     public long getTimestamp()
219     {
220         return timestamp;
221     }
222 
223 
224     /**
225      * @return The replicaId
226      */
227     public String getReplicaId()
228     {
229         return replicaId;
230     }
231 
232 
233     /**
234      * @return The operation sequence
235      */
236     public int getOperationSequence()
237     {
238         return operationSequence;
239     }
240 
241 
242     /**
243      * @return The CSN as a String
244      */
245     public String toString()
246     {
247         return toOctetString();
248     }
249 
250 
251     /**
252      * Returns a hash code value for the object.
253      * 
254      * @return a hash code value for this object.
255      */
256     public int hashCode()
257     {
258         return replicaId.hashCode() ^ ( int ) timestamp ^ operationSequence;
259     }
260 
261 
262     /**
263      * Indicates whether some other object is "equal to" this one
264      * 
265      * @param o the reference object with which to compare.
266      * @return <code>true</code> if this object is the same as the obj argument; 
267      * <code>false</code> otherwise.
268      */
269     public boolean equals( Object o )
270     {
271         if ( o == null )
272         {
273             return false;
274         }
275 
276         if ( this == o )
277         {
278             return true;
279         }
280 
281         if ( !( o instanceof CSN ) )
282         {
283             return false;
284         }
285 
286         CSN that = ( CSN ) o;
287 
288         return timestamp == that.getTimestamp() && replicaId.equals( that.getReplicaId() )
289             && operationSequence == that.getOperationSequence();
290     }
291 
292 
293     /**
294      * Compares this object with the specified object for order.  Returns a
295      * negative integer, zero, or a positive integer as this object is less
296      * than, equal to, or greater than the specified object.<p>
297      * 
298      * @param   o the Object to be compared.
299      * @return  a negative integer, zero, or a positive integer as this object
300      *      is less than, equal to, or greater than the specified object.
301      */
302     public int compareTo( CSN csn )
303     {
304         if ( csn == null )
305         {
306             return 1;
307         }
308         
309         long thatTimestamp = csn.getTimestamp();
310 
311         if ( this.timestamp < thatTimestamp )
312         {
313             return -1;
314         }
315         else if ( this.timestamp > thatTimestamp )
316         {
317             return 1;
318         }
319 
320         int replicaIdCompareResult = this.replicaId.compareTo( csn.getReplicaId() );
321 
322         if ( replicaIdCompareResult != 0 )
323         {
324             return replicaIdCompareResult;
325         }
326 
327         int thatSequence = csn.getOperationSequence();
328 
329         if ( this.operationSequence < thatSequence )
330         {
331             return -1;
332         }
333         else if ( this.operationSequence > thatSequence )
334         {
335             return 1;
336         }
337         else
338         {
339             return 0;
340         }
341     }
342 }