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 }