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.operation;
21  
22  
23  import java.io.Externalizable;
24  import java.io.IOException;
25  import java.io.ObjectInput;
26  import java.io.ObjectOutput;
27  
28  import javax.naming.NamingException;
29  import javax.naming.OperationNotSupportedException;
30  
31  import org.apache.directory.mitosis.common.CSN;
32  import org.apache.directory.mitosis.common.Constants;
33  import org.apache.directory.mitosis.store.ReplicationStore;
34  import org.apache.directory.server.core.CoreSession;
35  import org.apache.directory.server.core.entry.DefaultServerAttribute;
36  import org.apache.directory.server.core.entry.DefaultServerEntry;
37  import org.apache.directory.server.core.partition.Partition;
38  import org.apache.directory.server.core.partition.PartitionNexus;
39  import org.apache.directory.server.schema.registries.Registries;
40  import org.apache.directory.shared.ldap.name.LdapDN;
41  import org.apache.directory.shared.ldap.name.LdapDNSerializer;
42  import org.apache.directory.shared.ldap.schema.AttributeType;
43  
44  
45  /**
46   * Represents an operation performed on one or more entries in replicated
47   * {@link Partition}.  Each {@link Operation} has its own {@link CSN} which
48   * identifies itself.
49   * <p>
50   * An {@link Operation} is usually created by calling factory methods in
51   * {@link OperationFactory}, which produces a {@link CompositeOperation} of
52   * smaller multiple operations.  For example,
53   * {@link OperationFactory#newDelete(LdapDN)} returns a
54   * {@link CompositeOperation} which consists of two
55   * {@link ReplaceAttributeOperation}s; one updates {@link Constants#ENTRY_CSN}
56   * attribute and the other updates {@link Constants#ENTRY_DELETED}.  Refer
57   * to {@link OperationFactory} to find out what LDAP/JNDI operation is 
58   * translated into what {@link Operation} instance.
59   *
60   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
61   */
62  public class Operation implements Externalizable
63  {
64      /**
65       * Declares the Serial Version Uid.
66       *
67       * @see <a
68       *      href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always
69       *      Declare Serial Version Uid</a>
70       */
71      private static final long serialVersionUID = 1L;
72  
73      /** The entry CSN */
74      protected CSN csn;
75      
76      /** The operation type */
77      protected OperationType operationType;
78      
79      /** A reference on the server registries */
80      protected Registries registries;
81  
82  
83      /**
84       * Creates a new instance of Operation, for the entry which
85       * csn is given as a parameter. This constructor is not visible
86       * out of this package, as it's only used for the deserialization 
87       * process.
88       *
89       * @param registries the server registries
90       * @param operationType the operation type
91       */
92      /* no qualifier */ Operation( Registries registries, OperationType operationType )
93      {
94          this.registries = registries;
95          this.operationType = operationType;
96      }
97  
98      
99      /**
100      * Creates a new instance of Operation, for the entry which
101      * csn is given as a parameter.
102      *
103      * @param registries the server registries
104      * @param operationType the operation type
105      * @param csn The entry's csn.
106      */
107     protected Operation( Registries registries, OperationType operationType, CSN csn )
108     {
109         assert csn != null;
110         this.registries = registries;
111         this.csn = csn;
112         this.operationType = operationType;
113     }
114 
115 
116     /**
117      * @return Returns {@link CSN} for this operation.
118      */
119     public CSN getCSN()
120     {
121         return csn;
122     }
123 
124 
125     /**
126      * Replicates this operation on the specified nexus.
127      * 
128      * @param nexus the partition nexus
129      * @param store the replication store
130      * @param coreSession the current session
131      */
132     public final void execute( PartitionNexus nexus, ReplicationStore store, CoreSession coreSession ) 
133         throws Exception
134     {
135         synchronized ( nexus )
136         {
137             execute0( nexus, store, coreSession );
138             store.putLog( this );
139         }
140     }
141 
142     /**
143      * Not supported. We should neve call this method directly.
144      * 
145      * @param nexus the partition nexus
146      * @param store the replication store
147      * @param coreSession the current session
148      * @throws Exception
149      */
150     protected void execute0( PartitionNexus nexus, ReplicationStore store, CoreSession coreSession ) 
151         throws Exception
152     {
153         throw new OperationNotSupportedException( nexus.getSuffixDn().toString() );
154     }
155 
156     
157     /**
158      * Deserialize an Attribute Operation
159      *
160      * @param in the stream from which we will read an AttributeOperation
161      * @param registries the server registries
162      * @param operation the operation we will feed
163      * @return an AttributeOperation
164      * @throws ClassNotFoundException 
165      * @throws IOException
166      */
167     private static Operation readAttributeOperation( ObjectInput in, Registries registries, 
168         Operation operation ) throws ClassNotFoundException, IOException
169     {
170         AttributeOperation attributeOperation = (AttributeOperation)operation;
171         // Read the DN
172         LdapDN dn = LdapDNSerializer.deserialize( in );
173         
174         // Read the Attribute ID
175         String id = in.readUTF();
176         
177         try
178         {
179             // Get the AttributeType
180             AttributeType at = registries.getAttributeTypeRegistry().lookup( id );
181             
182             // Deserialize the attribute
183             DefaultServerAttribute attribute = new DefaultServerAttribute( id, at );
184             attribute.deserialize( in );
185             
186             // Store the read data into the operation 
187             attributeOperation.dn = dn;
188             attributeOperation.attribute = attribute; 
189                 
190             return operation;
191         }
192         catch ( NamingException ne )
193         {
194             throw new IOException( "Cannot find the '" + id + "' attributeType" ); 
195         }
196     }
197     
198     
199     /**
200      * Deserialize an operation. This is a recursive method, as we may have 
201      * composite operations.
202      *
203      * @param registries The server registries
204      * @param in the stream wrom which we will read an operation
205      * @return an operation
206      * @throws ClassNotFoundException
207      * @throws IOException
208      */
209     public static Operation deserialize( Registries registries, ObjectInput in ) throws ClassNotFoundException, IOException
210     {
211         // Read the operation type
212         int opTypeValue = in.readInt();
213         OperationType opType = OperationType.get( opTypeValue );
214         
215         // Read the CSN
216         CSN csn = (CSN)in.readObject();
217         
218         Operation operation = null;
219 
220         switch ( opType )
221         {
222             case ADD_ATTRIBUTE :
223                 // Create a new AddAttribute operation
224                 operation = new AddAttributeOperation( registries );
225                 
226                 // Set the CSN
227                 operation.csn = csn;
228                 
229                 // Read it
230                 readAttributeOperation( in, registries, operation );
231                 
232                 return operation;
233                 
234             case DELETE_ATTRIBUTE :
235                 // Create a new DeleteAttribute operation
236                 operation = new DeleteAttributeOperation( registries );
237                 
238                 // Set the CSN
239                 operation.csn = csn;
240                 
241                 // Read it
242                 readAttributeOperation( in, registries, operation );
243                 
244                 return operation;
245         
246             case REPLACE_ATTRIBUTE :
247                 // Create a new ReplaceAttribute operation
248                 operation = new ReplaceAttributeOperation( registries );
249                 
250                 // Set the CSN
251                 operation.csn = csn;
252                 
253                 // Read it
254                 readAttributeOperation( in, registries, operation );
255                 
256                 return operation;
257         
258             case ADD_ENTRY :
259                 // Create a new AddEntry operation
260                 operation = new AddEntryOperation( registries );
261                 
262                 // Set the CSN
263                 operation.csn = csn;
264                 
265                 DefaultServerEntry entry = new DefaultServerEntry( registries );
266                 entry.deserialize( in );
267                 ((AddEntryOperation)operation).setEntry( entry );
268                 
269                 return operation;
270         
271             case COMPOSITE_OPERATION :
272                 // Create a new Composite operation
273                 operation = new CompositeOperation( registries );
274                 
275                 // Set the CSN
276                 operation.csn = csn;
277 
278                 // Read the number of operations to deserialize
279                 int nbOperations = in.readInt();
280                 
281                 for ( int i = 0; i < nbOperations; i++ )
282                 {
283                     Operation child = deserialize( registries, in );
284                     child.csn = csn;
285                     ((CompositeOperation)operation).add( child );
286                 }
287             
288                 return operation;
289             
290             default :
291                 throw new IOException( "Cannot read the unkown operation" );
292         }
293     }
294     
295 
296     /**
297      * Serialize an operation. This is a recursive method, as an operation
298      * can be composite.
299      *
300      * @param operation the operation to serialize
301      * @param out the stream into which the resulting serialized operation will be stored
302      * @throws ClassNotFoundException
303      * @throws IOException
304      */
305     public static void serialize( Operation operation, ObjectOutput out ) throws ClassNotFoundException, IOException
306     {
307         OperationType opType = operation.operationType;
308         
309         // Write the operation type
310         out.writeInt( opType.ordinal() );
311         
312         // Write the CSN
313         out.writeObject( operation.csn );
314         
315         switch ( opType )
316         {
317             case REPLACE_ATTRIBUTE :
318             case DELETE_ATTRIBUTE :
319             case ADD_ATTRIBUTE :
320                 AttributeOperation attrOp = (AttributeOperation)operation;
321                 
322                 // Write the DN
323                 LdapDNSerializer.serialize( attrOp.dn, out );
324                 
325                 // Write the attribute ID
326                 out.writeUTF( ((AttributeOperation)operation).attribute.getId() );
327                 
328                 // Write the attribute
329                 DefaultServerAttribute attr = (DefaultServerAttribute)(attrOp.attribute);
330                 attr.serialize( out );
331                 return;
332                 
333             case ADD_ENTRY :
334                 ((DefaultServerEntry)((AddEntryOperation)operation).getEntry()).serialize( out );
335                 return;
336                 
337             case COMPOSITE_OPERATION :
338                 out.writeInt( ((CompositeOperation)operation).size() );
339                 
340                 // Loop on all the operations
341                 for ( Operation child:((CompositeOperation)operation).getChildren() )
342                 {
343                     serialize( child, out );
344                 }
345                 
346                 return;
347         }
348     }
349     
350     
351     /**
352      * Read the CSN from an input stream
353      * 
354      * @param in the input stream
355      * @throws ClassNotFoundException if the read object is not a CSN
356      * @throws IOException if we can't read from the input stream
357      */
358     public void readExternal( ObjectInput in ) throws ClassNotFoundException, IOException
359     {
360         csn = (CSN)in.readObject();
361     }
362     
363     
364     /**
365      * Write the CSN to an output stream
366      * 
367      * @param out the output stream in which the CSN is written
368      * @throws IOException if we can't write to the stream
369      */
370     public void writeExternal( ObjectOutput out) throws IOException
371     {
372         out.writeObject( csn );
373     }
374 
375 
376     /**
377      * @see Object#toString()
378      */
379     public String toString()
380     {
381         return csn.toString();
382     }
383 }