001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2007-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.core;
028 import org.opends.messages.MessageBuilder;
029
030 import static org.opends.server.core.CoreConstants.*;
031 import static org.opends.server.loggers.AccessLogger.logCompareRequest;
032 import static org.opends.server.loggers.AccessLogger.logCompareResponse;
033 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
034 import static org.opends.messages.CoreMessages.*;
035 import java.util.ArrayList;
036 import java.util.Iterator;
037 import java.util.List;
038
039 import org.opends.server.api.ClientConnection;
040 import org.opends.server.api.plugin.PluginResult;
041 import org.opends.server.loggers.debug.DebugLogger;
042 import org.opends.server.loggers.debug.DebugTracer;
043 import org.opends.server.protocols.asn1.ASN1OctetString;
044 import org.opends.server.types.*;
045 import org.opends.server.types.operation.PostResponseCompareOperation;
046 import org.opends.server.types.operation.PreParseCompareOperation;
047 import org.opends.server.workflowelement.localbackend.
048 LocalBackendCompareOperation;
049
050
051 /**
052 * This class defines an operation that may be used to determine whether a
053 * specified entry in the Directory Server contains a given attribute-value
054 * pair.
055 */
056 public class CompareOperationBasis
057 extends AbstractOperation
058 implements PreParseCompareOperation, CompareOperation,
059 Runnable, PostResponseCompareOperation
060 {
061 /**
062 * The tracer object for the debug logger.
063 */
064 private static final DebugTracer TRACER = DebugLogger.getTracer();
065
066 // The attribute type for this compare operation.
067 private AttributeType attributeType;
068
069 // The assertion value for the compare operation.
070 private ByteString assertionValue;
071
072 // The raw, unprocessed entry DN as included in the client request.
073 private ByteString rawEntryDN;
074
075 // The DN of the entry for the compare operation.
076 private DN entryDN;
077
078 // The proxied authorization target DN for this operation.
079 private DN proxiedAuthorizationDN;
080
081 // The set of response controls for this compare operation.
082 private List<Control> responseControls;
083
084 // The attribute type for the compare operation.
085 private String rawAttributeType;
086
087
088
089 /**
090 * Creates a new compare operation with the provided information.
091 *
092 * @param clientConnection The client connection with which this operation
093 * is associated.
094 * @param operationID The operation ID for this operation.
095 * @param messageID The message ID of the request with which this
096 * operation is associated.
097 * @param requestControls The set of controls included in the request.
098 * @param rawEntryDN The raw, unprocessed entry DN as provided in the
099 * client request. This may or may not be a valid
100 * DN as no validation will have been performed yet.
101 * @param rawAttributeType The raw attribute type for the compare operation.
102 * @param assertionValue The assertion value for the compare operation.
103 */
104 public CompareOperationBasis(
105 ClientConnection clientConnection, long operationID,
106 int messageID, List<Control> requestControls,
107 ByteString rawEntryDN, String rawAttributeType,
108 ByteString assertionValue)
109 {
110 super(clientConnection, operationID, messageID, requestControls);
111
112
113 this.rawEntryDN = rawEntryDN;
114 this.rawAttributeType = rawAttributeType;
115 this.assertionValue = assertionValue;
116
117 responseControls = new ArrayList<Control>();
118 entryDN = null;
119 attributeType = null;
120 cancelRequest = null;
121 proxiedAuthorizationDN = null;
122 }
123
124
125
126 /**
127 * Creates a new compare operation with the provided information.
128 *
129 * @param clientConnection The client connection with which this operation
130 * is associated.
131 * @param operationID The operation ID for this operation.
132 * @param messageID The message ID of the request with which this
133 * operation is associated.
134 * @param requestControls The set of controls included in the request.
135 * @param entryDN The entry DN for this compare operation.
136 * @param attributeType The attribute type for this compare operation.
137 * @param assertionValue The assertion value for the compare operation.
138 */
139 public CompareOperationBasis(
140 ClientConnection clientConnection, long operationID,
141 int messageID, List<Control> requestControls,
142 DN entryDN, AttributeType attributeType,
143 ByteString assertionValue)
144 {
145 super(clientConnection, operationID, messageID, requestControls);
146
147
148 this.entryDN = entryDN;
149 this.attributeType = attributeType;
150 this.assertionValue = assertionValue;
151
152 responseControls = new ArrayList<Control>();
153 rawEntryDN = new ASN1OctetString(entryDN.toString());
154 rawAttributeType = attributeType.getNameOrOID();
155 cancelRequest = null;
156 proxiedAuthorizationDN = null;
157 }
158
159
160
161 /**
162 * {@inheritDoc}
163 */
164 public final ByteString getRawEntryDN()
165 {
166 return rawEntryDN;
167 }
168
169
170
171 /**
172 * {@inheritDoc}
173 */
174 public final void setRawEntryDN(ByteString rawEntryDN)
175 {
176 this.rawEntryDN = rawEntryDN;
177
178 entryDN = null;
179 }
180
181
182
183 /**
184 * {@inheritDoc}
185 */
186 public final DN getEntryDN()
187 {
188 return entryDN;
189 }
190
191
192
193 /**
194 * {@inheritDoc}
195 */
196 public final String getRawAttributeType()
197 {
198 return rawAttributeType;
199 }
200
201
202
203 /**
204 * {@inheritDoc}
205 */
206 public final void setRawAttributeType(String rawAttributeType)
207 {
208 this.rawAttributeType = rawAttributeType;
209
210 attributeType = null;
211 }
212
213
214
215 /**
216 * {@inheritDoc}
217 */
218 public final AttributeType getAttributeType()
219 {
220 return attributeType;
221 }
222
223
224
225 /**
226 * {@inheritDoc}
227 */
228 public void setAttributeType(AttributeType attributeType)
229 {
230 this.attributeType = attributeType;
231 }
232
233
234
235 /**
236 * {@inheritDoc}
237 */
238 public final ByteString getAssertionValue()
239 {
240 return assertionValue;
241 }
242
243
244
245 /**
246 * {@inheritDoc}
247 */
248 public final void setAssertionValue(ByteString assertionValue)
249 {
250 this.assertionValue = assertionValue;
251 }
252
253
254
255 /**
256 * {@inheritDoc}
257 */
258 @Override()
259 public final OperationType getOperationType()
260 {
261 // Note that no debugging will be done in this method because it is a likely
262 // candidate for being called by the logging subsystem.
263
264 return OperationType.COMPARE;
265 }
266
267
268
269 /**
270 * {@inheritDoc}
271 */
272 @Override()
273 public final String[][] getRequestLogElements()
274 {
275 // Note that no debugging will be done in this method because it is a likely
276 // candidate for being called by the logging subsystem.
277
278 return new String[][]
279 {
280 new String[] { LOG_ELEMENT_ENTRY_DN, String.valueOf(rawEntryDN) },
281 new String[] { LOG_ELEMENT_COMPARE_ATTR, rawAttributeType }
282 };
283 }
284
285
286
287 /**
288 * {@inheritDoc}
289 */
290 @Override()
291 public final String[][] getResponseLogElements()
292 {
293 // Note that no debugging will be done in this method because it is a likely
294 // candidate for being called by the logging subsystem.
295
296 String resultCode = String.valueOf(getResultCode().getIntValue());
297
298 String errorMessage;
299 MessageBuilder errorMessageBuffer = getErrorMessage();
300 if (errorMessageBuffer == null)
301 {
302 errorMessage = null;
303 }
304 else
305 {
306 errorMessage = errorMessageBuffer.toString();
307 }
308
309 String matchedDNStr;
310 DN matchedDN = getMatchedDN();
311 if (matchedDN == null)
312 {
313 matchedDNStr = null;
314 }
315 else
316 {
317 matchedDNStr = matchedDN.toString();
318 }
319
320 String referrals;
321 List<String> referralURLs = getReferralURLs();
322 if ((referralURLs == null) || referralURLs.isEmpty())
323 {
324 referrals = null;
325 }
326 else
327 {
328 StringBuilder buffer = new StringBuilder();
329 Iterator<String> iterator = referralURLs.iterator();
330 buffer.append(iterator.next());
331
332 while (iterator.hasNext())
333 {
334 buffer.append(", ");
335 buffer.append(iterator.next());
336 }
337
338 referrals = buffer.toString();
339 }
340
341 String processingTime =
342 String.valueOf(getProcessingTime());
343
344 return new String[][]
345 {
346 new String[] { LOG_ELEMENT_RESULT_CODE, resultCode },
347 new String[] { LOG_ELEMENT_ERROR_MESSAGE, errorMessage },
348 new String[] { LOG_ELEMENT_MATCHED_DN, matchedDNStr },
349 new String[] { LOG_ELEMENT_REFERRAL_URLS, referrals },
350 new String[] { LOG_ELEMENT_PROCESSING_TIME, processingTime }
351 };
352 }
353
354
355
356 /**
357 * Retrieves the proxied authorization DN for this operation if proxied
358 * authorization has been requested.
359 *
360 * @return The proxied authorization DN for this operation if proxied
361 * authorization has been requested, or {@code null} if proxied
362 * authorization has not been requested.
363 */
364 public DN getProxiedAuthorizationDN()
365 {
366 return proxiedAuthorizationDN;
367 }
368
369
370
371 /**
372 * {@inheritDoc}
373 */
374 public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
375 {
376 this.proxiedAuthorizationDN = proxiedAuthorizationDN;
377 }
378
379
380
381 /**
382 * {@inheritDoc}
383 */
384 @Override()
385 public final List<Control> getResponseControls()
386 {
387 return responseControls;
388 }
389
390
391
392 /**
393 * {@inheritDoc}
394 */
395 @Override()
396 public final void addResponseControl(Control control)
397 {
398 responseControls.add(control);
399 }
400
401
402
403 /**
404 * {@inheritDoc}
405 */
406 @Override()
407 public final void removeResponseControl(Control control)
408 {
409 responseControls.remove(control);
410 }
411
412
413
414 /**
415 * Performs the work of actually processing this operation. This
416 * should include all processing for the operation, including
417 * invoking plugins, logging messages, performing access control,
418 * managing synchronization, and any other work that might need to
419 * be done in the course of processing.
420 */
421 public final void run()
422 {
423 setResultCode(ResultCode.UNDEFINED);
424
425 // Start the processing timer.
426 setProcessingStartTime();
427
428 // Log the compare request message.
429 logCompareRequest(this);
430
431 // Get the plugin config manager that will be used for invoking plugins.
432 PluginConfigManager pluginConfigManager =
433 DirectoryServer.getPluginConfigManager();
434
435 // This flag is set to true as soon as a workflow has been executed.
436 boolean workflowExecuted = false;
437
438 try
439 {
440 // Check for and handle a request to cancel this operation.
441 checkIfCanceled(false);
442
443 // Invoke the pre-parse compare plugins.
444 PluginResult.PreParse preParseResult =
445 pluginConfigManager.invokePreParseComparePlugins(this);
446 if(!preParseResult.continueProcessing())
447 {
448 setResultCode(preParseResult.getResultCode());
449 appendErrorMessage(preParseResult.getErrorMessage());
450 setMatchedDN(preParseResult.getMatchedDN());
451 setReferralURLs(preParseResult.getReferralURLs());
452 return;
453 }
454
455
456 // Check for a request to cancel this operation.
457 checkIfCanceled(false);
458
459
460 // Process the entry DN to convert it from the raw form to the form
461 // required for the rest of the compare processing.
462 try
463 {
464 if (entryDN == null)
465 {
466 entryDN = DN.decode(rawEntryDN);
467 }
468 }
469 catch (DirectoryException de)
470 {
471 if (debugEnabled())
472 {
473 TRACER.debugCaught(DebugLogLevel.ERROR, de);
474 }
475
476 setResultCode(de.getResultCode());
477 appendErrorMessage(de.getMessageObject());
478
479 return;
480 }
481
482
483 // Retrieve the network group registered with the client connection
484 // and get a workflow to process the operation.
485 NetworkGroup ng = getClientConnection().getNetworkGroup();
486 Workflow workflow = ng.getWorkflowCandidate(entryDN);
487 if (workflow == null)
488 {
489 // We have found no workflow for the requested base DN, just return
490 // a no such entry result code and stop the processing.
491 updateOperationErrMsgAndResCode();
492 return;
493 }
494 workflow.execute(this);
495 workflowExecuted = true;
496
497 }
498 catch(CanceledOperationException coe)
499 {
500 if (debugEnabled())
501 {
502 TRACER.debugCaught(DebugLogLevel.ERROR, coe);
503 }
504
505 setResultCode(ResultCode.CANCELED);
506 cancelResult = new CancelResult(ResultCode.CANCELED, null);
507
508 appendErrorMessage(coe.getCancelRequest().getCancelReason());
509 }
510 finally
511 {
512 // Stop the processing timer.
513 setProcessingStopTime();
514
515 if(cancelRequest == null || cancelResult == null ||
516 cancelResult.getResultCode() != ResultCode.CANCELED ||
517 cancelRequest.notifyOriginalRequestor() ||
518 DirectoryServer.notifyAbandonedOperations())
519 {
520 clientConnection.sendResponse(this);
521 }
522
523
524 // Log the compare response message.
525 logCompareResponse(this);
526
527 // Invoke the post-response compare plugins.
528 invokePostResponsePlugins(workflowExecuted);
529
530 // If no cancel result, set it
531 if(cancelResult == null)
532 {
533 cancelResult = new CancelResult(ResultCode.TOO_LATE, null);
534 }
535 }
536 }
537
538
539 /**
540 * Invokes the post response plugins. If a workflow has been executed
541 * then invoke the post response plugins provided by the workflow
542 * elements of the worklfow, otherwise invoke the post reponse plugins
543 * that have been registered with the current operation.
544 *
545 * @param workflowExecuted <code>true</code> if a workflow has been
546 * executed
547 */
548 private void invokePostResponsePlugins(boolean workflowExecuted)
549 {
550 // Get the plugin config manager that will be used for invoking plugins.
551 PluginConfigManager pluginConfigManager =
552 DirectoryServer.getPluginConfigManager();
553
554 // Invoke the post response plugins
555 if (workflowExecuted)
556 {
557 // Invoke the post response plugins that have been registered by
558 // the workflow elements
559 List localOperations =
560 (List)getAttachment(Operation.LOCALBACKENDOPERATIONS);
561
562 if (localOperations != null)
563 {
564 for (Object localOp : localOperations)
565 {
566 LocalBackendCompareOperation localOperation =
567 (LocalBackendCompareOperation)localOp;
568 pluginConfigManager.invokePostResponseComparePlugins(localOperation);
569 }
570 }
571 }
572 else
573 {
574 // Invoke the post response plugins that have been registered with
575 // the current operation
576 pluginConfigManager.invokePostResponseComparePlugins(this);
577 }
578 }
579
580
581 /**
582 * Updates the error message and the result code of the operation.
583 *
584 * This method is called because no workflow was found to process
585 * the operation.
586 */
587 private void updateOperationErrMsgAndResCode()
588 {
589 setResultCode(ResultCode.NO_SUCH_OBJECT);
590 appendErrorMessage(
591 ERR_COMPARE_NO_SUCH_ENTRY.get(String.valueOf(getEntryDN())));
592 }
593
594
595
596 /**
597 * {@inheritDoc}
598 */
599 @Override()
600 public final void toString(StringBuilder buffer)
601 {
602 buffer.append("CompareOperation(connID=");
603 buffer.append(clientConnection.getConnectionID());
604 buffer.append(", opID=");
605 buffer.append(operationID);
606 buffer.append(", dn=");
607 buffer.append(rawEntryDN);
608 buffer.append(", attr=");
609 buffer.append(rawAttributeType);
610 buffer.append(")");
611 }
612
613
614 /**
615 * {@inheritDoc}
616 *
617 * This method always returns null.
618 */
619 public Entry getEntryToCompare()
620 {
621 return null;
622 }
623
624 }