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 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.types;
028 import org.opends.messages.Message;
029 import org.opends.messages.MessageBuilder;
030
031
032 import static org.opends.server.core.CoreConstants.*;
033
034 import java.util.ArrayList;
035 import java.util.HashMap;
036 import java.util.List;
037 import java.util.Map;
038 import org.opends.server.api.ClientConnection;
039 import org.opends.server.types.operation.PostResponseOperation;
040 import org.opends.server.types.operation.PreParseOperation;
041 import org.opends.server.core.DirectoryServer;
042 import static org.opends.server.loggers.debug.
043 DebugLogger.debugEnabled;
044 import static org.opends.server.loggers.debug.DebugLogger.getTracer;
045 import org.opends.server.loggers.debug.DebugTracer;
046
047
048 /**
049 * This class defines a generic operation that may be processed by the
050 * Directory Server. Specific subclasses should implement specific
051 * functionality appropriate for the type of operation.
052 * <BR><BR>
053 * Note that this class is not intended to be subclassed by any
054 * third-party code outside of the OpenDS project. It should only be
055 * extended by the operation types included in the
056 * {@code org.opends.server.core} package.
057 */
058 @org.opends.server.types.PublicAPI(
059 stability=org.opends.server.types.StabilityLevel.VOLATILE,
060 mayInstantiate=false,
061 mayExtend=false,
062 mayInvoke=true)
063 public abstract class AbstractOperation
064 implements Operation, PreParseOperation, PostResponseOperation,
065 Runnable
066 {
067 /**
068 * The tracer object for the debug logger.
069 */
070 private static final DebugTracer TRACER = getTracer();
071
072 /**
073 * The set of response controls that will always be returned for
074 * an abandon operation.
075 */
076 protected static final List<Control> NO_RESPONSE_CONTROLS =
077 new ArrayList<Control>(0);
078
079
080 /**
081 * The client connection with which this operation is associated.
082 */
083 protected final ClientConnection clientConnection;
084
085
086 /**
087 * The message ID for this operation.
088 */
089 protected final int messageID;
090
091
092
093 /**
094 * The operation ID for this operation.
095 */
096 protected final long operationID;
097
098
099 /**
100 * Wether nanotime was used for this operation.
101 */
102 protected final boolean useNanoTime;
103
104
105 /**
106 * The cancel request for this operation.
107 */
108 protected CancelRequest cancelRequest;
109
110
111 /**
112 * The cancel result for this operation.
113 */
114 protected CancelResult cancelResult;
115
116 // Indicates whether this is an internal operation triggered within
117 // the server itself rather than requested by an external client.
118 private boolean isInternalOperation;
119
120 // Indicates whether this operation is involved in data
121 // synchronization processing.
122 private boolean isSynchronizationOperation;
123
124 // The matched DN for this operation.
125 private DN matchedDN;
126
127 // The entry for the authorization identify for this operation.
128 private Entry authorizationEntry;
129
130 // A set of attachments associated with this operation that might
131 // be used by various components during its processing.
132 private Map<String,Object> attachments;
133
134 // The set of controls included in the request from the client.
135 private List<Control> requestControls;
136
137 // The set of referral URLs for this operation.
138 private List<String> referralURLs;
139
140 // The result code for this operation.
141 private ResultCode resultCode;
142
143 // Additional information that should be included in the log but
144 // not sent to the client.
145 private MessageBuilder additionalLogMessage;
146
147 // The error message for this operation that should be included in
148 // the log and in the response to the client.
149 private MessageBuilder errorMessage;
150
151 // Indicates whether this operation nneds to be synchronized to
152 // other copies of the data.
153 private boolean dontSynchronizeFlag;
154
155 // The time that processing started on this operation in
156 // milliseconds.
157 private long processingStartTime;
158
159 // The time that processing ended on this operation in
160 // milliseconds.
161 private long processingStopTime;
162
163 // The time that processing started on this operation in
164 // nanoseconds.
165 private long processingStartNanoTime;
166
167 // The time that processing ended on this operation in
168 // nanoseconds.
169 private long processingStopNanoTime;
170
171 /**
172 * Creates a new operation with the provided information.
173 *
174 * @param clientConnection The client connection with which this
175 * operation is associated.
176 * @param operationID The identifier assigned to this
177 * operation for the client connection.
178 * @param messageID The message ID of the request with
179 * which this operation is associated.
180 * @param requestControls The set of controls included in the
181 * request.
182 */
183 protected AbstractOperation(ClientConnection clientConnection,
184 long operationID,
185 int messageID, List<Control> requestControls)
186 {
187 this.clientConnection = clientConnection;
188 this.operationID = operationID;
189 this.messageID = messageID;
190 this.useNanoTime = DirectoryServer.getUseNanoTime();
191
192 if (requestControls == null)
193 {
194 this.requestControls = new ArrayList<Control>(0);
195 }
196 else
197 {
198 this.requestControls = requestControls;
199 }
200
201 resultCode = ResultCode.UNDEFINED;
202 additionalLogMessage = new MessageBuilder();
203 errorMessage = new MessageBuilder();
204 attachments = new HashMap<String,Object>();
205 matchedDN = null;
206 referralURLs = null;
207 cancelResult = null;
208 isInternalOperation = false;
209 isSynchronizationOperation = false;
210 authorizationEntry =
211 clientConnection.getAuthenticationInfo().
212 getAuthorizationEntry();
213 }
214
215
216
217 /**
218 * Retrieves the operation type for this operation.
219 *
220 * @return The operation type for this operation.
221 */
222 public abstract OperationType getOperationType();
223
224
225
226 /**
227 * Terminates the client connection being used to process this
228 * operation. If this is called by a plugin, then that plugin must
229 * return a result indicating that the client connection has been
230 * teriminated.
231 *
232 * @param disconnectReason The disconnect reason that provides the
233 * generic cause for the disconnect.
234 * @param sendNotification Indicates whether to try to provide
235 * notification
236 * to the client that the connection will
237 * be closed.
238 * @param message The message to send to the client. It
239 * may be {@code null} if no notification
240 * is to be sent.
241 */
242 public void disconnectClient(DisconnectReason disconnectReason,
243 boolean sendNotification,
244 Message message)
245 {
246 clientConnection.disconnect(disconnectReason, sendNotification,
247 message);
248 }
249
250
251
252 /**
253 * Retrieves a set of standard elements that should be logged in all
254 * requests and responses for all types of operations. Each element
255 * in the array will itself be a two-element array in which the
256 * first element is the name of the field and the second is a string
257 * representation of the value, or {@code null} if there is no value
258 * for that field.
259 *
260 * @return A standard set of elements that should be logged in
261 * requests and responses for all types of operations.
262 */
263 public final String[][] getCommonLogElements()
264 {
265 // Note that no debugging will be done in this method because
266 // it is a likely candidate for being called by the logging
267 // subsystem.
268
269 return new String[][]
270 {
271 new String[] { LOG_ELEMENT_CONNECTION_ID,
272 String.valueOf(getConnectionID()) },
273 new String[] { LOG_ELEMENT_OPERATION_ID,
274 String.valueOf(operationID) },
275 new String[] { LOG_ELEMENT_MESSAGE_ID,
276 String.valueOf(messageID) }
277 };
278 }
279
280
281
282 /**
283 * Retrieves a standard set of elements that should be logged in
284 * requests for this type of operation. Each element in the array
285 * will itself be a two-element array in which the first element is
286 * the name of the field and the second is a string representation
287 * of the value, or {@code null} if there is no value for that
288 * field.
289 *
290 * @return A standard set of elements that should be logged in
291 * requests for this type of operation.
292 */
293 public abstract String[][] getRequestLogElements();
294
295
296
297 /**
298 * Retrieves a standard set of elements that should be logged in
299 * responses for this type of operation. Each element in the array
300 * will itself be a two-element array in which the first element is
301 * the name of the field and the second is a string representation
302 * of the value, or {@code null} if there is no value for that
303 * field.
304 *
305 * @return A standard set of elements that should be logged in
306 * responses for this type of operation.
307 */
308 public abstract String[][] getResponseLogElements();
309
310
311
312 /**
313 * Retrieves the client connection with which this operation is
314 * associated.
315 *
316 * @return The client connection with which this operation is
317 * associated.
318 */
319 public final ClientConnection getClientConnection()
320 {
321 return clientConnection;
322 }
323
324
325
326 /**
327 * Retrieves the unique identifier that is assigned to the client
328 * connection that submitted this operation.
329 *
330 * @return The unique identifier that is assigned to the client
331 * connection that submitted this operation.
332 */
333 public final long getConnectionID()
334 {
335 return clientConnection.getConnectionID();
336 }
337
338
339
340 /**
341 * Retrieves the operation ID for this operation.
342 *
343 * @return The operation ID for this operation.
344 */
345 public final long getOperationID()
346 {
347 return operationID;
348 }
349
350
351
352 /**
353 * Retrieves the message ID assigned to this operation.
354 *
355 * @return The message ID assigned to this operation.
356 */
357 public final int getMessageID()
358 {
359 return messageID;
360 }
361
362
363
364 /**
365 * Retrieves the set of controls included in the request from the
366 * client. The returned list must not be altered.
367 *
368 * @return The set of controls included in the request from the
369 * client.
370 */
371 public final List<Control> getRequestControls()
372 {
373 return requestControls;
374 }
375
376
377
378 /**
379 * Adds the provided control to the set of request controls for this
380 * operation. This method may only be called by pre-parse plugins.
381 *
382 * @param control The control to add to the set of request
383 * controls for this operation.
384 */
385 public final void addRequestControl(Control control)
386 {
387 requestControls.add(control);
388 }
389
390
391
392 /**
393 * Removes the provided control from the set of request controls for
394 * this operation. This method may only be called by pre-parse
395 * plugins.
396 *
397 * @param control The control to remove from the set of request
398 * controls for this operation.
399 */
400 public final void removeRequestControl(Control control)
401 {
402 requestControls.remove(control);
403 }
404
405
406
407 /**
408 * Retrieves the set of controls to include in the response to the
409 * client. The contents of this list must not be altered.
410 *
411 * @return The set of controls to include in the response to the
412 * client.
413 */
414 public abstract List<Control> getResponseControls();
415
416
417
418 /**
419 * Adds the provided control to the set of controls to include in
420 * the response to the client. This method may not be called by
421 * post-response plugins.
422 *
423 * @param control The control to add to the set of controls to
424 * include in the response to the client.
425 */
426 public abstract void addResponseControl(Control control);
427
428
429
430 /**
431 * Removes the provided control from the set of controls to include
432 * in the response to the client. This method may not be called by
433 * post-response plugins.
434 *
435 * @param control The control to remove from the set of controls
436 * to include in the response to the client.
437 */
438 public abstract void removeResponseControl(Control control);
439
440
441
442 /**
443 * Retrieves the result code for this operation.
444 *
445 * @return The result code associated for this operation, or
446 * {@code UNDEFINED} if the operation has not yet
447 * completed.
448 */
449 public final ResultCode getResultCode()
450 {
451 return resultCode;
452 }
453
454
455
456 /**
457 * Specifies the result code for this operation. This method may
458 * not be called by post-response plugins.
459 *
460 * @param resultCode The result code for this operation.
461 */
462 public final void setResultCode(ResultCode resultCode)
463 {
464 this.resultCode = resultCode;
465 }
466
467
468
469 /**
470 * Retrieves the error message for this operation. Its contents may
471 * be altered by pre-parse, pre-operation, and post-operation
472 * plugins, but not by post-response plugins.
473 *
474 * @return The error message for this operation.
475 */
476 public final MessageBuilder getErrorMessage()
477 {
478 return errorMessage;
479 }
480
481
482
483 /**
484 * Specifies the error message for this operation. This method may
485 * not be called by post-response plugins.
486 *
487 * @param errorMessage The error message for this operation.
488 */
489 public final void setErrorMessage(MessageBuilder errorMessage)
490 {
491 if (errorMessage == null)
492 {
493 this.errorMessage = new MessageBuilder();
494 }
495 else
496 {
497 this.errorMessage = errorMessage;
498 }
499 }
500
501
502
503 /**
504 * Appends the provided message to the error message buffer. If the
505 * buffer has not yet been created, then this will create it first
506 * and then add the provided message. This method may not be called
507 * by post-response plugins.
508 *
509 * @param message The message to append to the error message
510 * buffer.
511 */
512 public final void appendErrorMessage(Message message)
513 {
514 if (errorMessage == null)
515 {
516 errorMessage = new MessageBuilder(message);
517 }
518 else
519 {
520 if (errorMessage.length() > 0)
521 {
522 errorMessage.append(" ");
523 }
524
525 errorMessage.append(message);
526 }
527 }
528
529
530
531 /**
532 * Retrieves the additional log message for this operation, which
533 * should be written to the log but not included in the response to
534 * the client. The contents of this buffer may be altered by
535 * pre-parse, pre-operation, and post-operation plugins, but not by
536 * post-response plugins.
537 *
538 * @return The additional log message for this operation.
539 */
540 public final MessageBuilder getAdditionalLogMessage()
541 {
542 return additionalLogMessage;
543 }
544
545
546
547 /**
548 * Specifies the additional log message for this operation, which
549 * should be written to the log but not included in the response to
550 * the client. This method may not be called by post-response
551 * plugins.
552 *
553 * @param additionalLogMessage The additional log message for this
554 * operation.
555 */
556 public final void setAdditionalLogMessage(
557 MessageBuilder additionalLogMessage)
558 {
559 if (additionalLogMessage == null)
560 {
561 this.additionalLogMessage = new MessageBuilder();
562 }
563 else
564 {
565 this.additionalLogMessage = additionalLogMessage;
566 }
567 }
568
569
570
571 /**
572 * Appends the provided message to the additional log information
573 * for this operation. This method may not be called by
574 * post-response plugins.
575 *
576 * @param message The message that should be appended to the
577 * additional log information for this operation.
578 */
579 public final void appendAdditionalLogMessage(Message message)
580 {
581 if (additionalLogMessage == null)
582 {
583 additionalLogMessage = new MessageBuilder(message);
584 }
585 else
586 {
587 additionalLogMessage.append(message);
588 }
589 }
590
591
592
593 /**
594 * Retrieves the matched DN for this operation.
595 *
596 * @return The matched DN for this operation, or {@code null} if
597 * the operation has not yet completed or does not have a
598 * matched DN.
599 */
600 public final DN getMatchedDN()
601 {
602 return matchedDN;
603 }
604
605
606
607 /**
608 * Specifies the matched DN for this operation. This may not be
609 * called by post-response plugins.
610 *
611 * @param matchedDN The matched DN for this operation.
612 */
613 public final void setMatchedDN(DN matchedDN)
614 {
615 this.matchedDN = matchedDN;
616 }
617
618
619
620 /**
621 * Retrieves the set of referral URLs for this operation. Its
622 * contents must not be altered by the caller.
623 *
624 * @return The set of referral URLs for this operation, or
625 * {@code null} if the operation is not yet complete or
626 * does not have a set of referral URLs.
627 */
628 public final List<String> getReferralURLs()
629 {
630 return referralURLs;
631 }
632
633
634
635 /**
636 * Specifies the set of referral URLs for this operation. This may
637 * not be called by post-response plugins.
638 *
639 * @param referralURLs The set of referral URLs for this
640 * operation.
641 */
642 public final void setReferralURLs(List<String> referralURLs)
643 {
644 this.referralURLs = referralURLs;
645 }
646
647
648
649 /**
650 * Sets the response elements for this operation based on the
651 * information contained in the provided {@code DirectoryException}
652 * object. This method may not be called by post-response plugins.
653 *
654 * @param directoryException The exception containing the
655 * information to use for the response
656 * elements.
657 */
658 public final void setResponseData(
659 DirectoryException directoryException)
660 {
661 this.resultCode = directoryException.getResultCode();
662 this.matchedDN = directoryException.getMatchedDN();
663 this.referralURLs = directoryException.getReferralURLs();
664
665 appendErrorMessage(directoryException.getMessageObject());
666 }
667
668
669
670 /**
671 * Indicates whether this is an internal operation rather than one
672 * that was requested by an external client.
673 *
674 * @return {@code true} if this is an internal operation, or
675 * {@code false} if it is not.
676 */
677 public final boolean isInternalOperation()
678 {
679 return isInternalOperation;
680 }
681
682
683
684 /**
685 * Specifies whether this is an internal operation rather than one
686 * that was requested by an external client. This may not be called
687 * from within a plugin.
688 *
689 * @param isInternalOperation Specifies whether this is an
690 * internal operation rather than one
691 * that was requested by an external
692 * client.
693 */
694 public final void setInternalOperation(boolean isInternalOperation)
695 {
696 this.isInternalOperation = isInternalOperation;
697 }
698
699
700
701 /**
702 * Indicates whether this is a synchronization operation rather than
703 * one that was requested by an external client.
704 *
705 * @return {@code true} if this is a data synchronization
706 * operation, or {@code false} if it is not.
707 */
708 public final boolean isSynchronizationOperation()
709 {
710 return isSynchronizationOperation;
711 }
712
713
714
715 /**
716 * Specifies whether this is a synchronization operation rather than
717 * one that was requested by an external client. This method may
718 * not be called from within a plugin.
719 *
720 * @param isSynchronizationOperation Specifies whether this is a
721 * synchronization operation
722 * rather than one that was
723 * requested by an external
724 * client.
725 */
726 public final void setSynchronizationOperation(
727 boolean isSynchronizationOperation)
728 {
729 this.isSynchronizationOperation = isSynchronizationOperation;
730 }
731
732
733
734 /**
735 * Indicates whether this operation needs to be synchronized to
736 * other copies of the data.
737 *
738 * @return {@code true} if this operation should not be
739 * synchronized, or {@code false} if it should be
740 * synchronized.
741 */
742 public boolean dontSynchronize()
743 {
744 return dontSynchronizeFlag;
745 }
746
747
748
749 /**
750 * Specifies whether this operation must be synchronized to other
751 * copies of the data.
752 *
753 * @param dontSynchronize Specifies whether this operation must be
754 * synchronized to other copies
755 * of the data.
756 */
757 public final void setDontSynchronize(boolean dontSynchronize)
758 {
759 this.dontSynchronizeFlag = dontSynchronize;
760 }
761
762
763
764 /**
765 * Retrieves the entry for the user that should be considered the
766 * authorization identity for this operation. In many cases, it
767 * will be the same as the authorization entry for the underlying
768 * client connection, or {@code null} if no authentication has been
769 * performed on that connection. However, it may be some other
770 * value if special processing has been requested (e.g., the
771 * operation included a proxied authorization control). This method
772 * should not be called by pre-parse plugins because the correct
773 * value may not yet have been determined.
774 *
775 * @return The entry for the user that should be considered the
776 * authorization identity for this operation, or
777 * {@code null} if the authorization identity should be the
778 * unauthenticated user.
779 */
780 public final Entry getAuthorizationEntry()
781 {
782 return authorizationEntry;
783 }
784
785
786
787 /**
788 * Provides the entry for the user that should be considered the
789 * authorization identity for this operation. This must not be
790 * called from within a plugin.
791 *
792 * @param authorizationEntry The entry for the user that should be
793 * considered the authorization identity
794 * for this operation, or {@code null}
795 * if it should be the unauthenticated
796 * user.
797 */
798 public final void setAuthorizationEntry(Entry authorizationEntry)
799 {
800 this.authorizationEntry = authorizationEntry;
801 }
802
803
804
805 /**
806 * Retrieves the authorization DN for this operation. In many
807 * cases, it will be the same as the DN of the authenticated user
808 * for the underlying connection, or the null DN if no
809 * authentication has been performed on that connection. However,
810 * it may be some other value if special processing has been
811 * requested (e.g., the operation included a proxied authorization
812 * control). This method should not be called by pre-parse plugins
813 * because the correct value may not have yet been determined.
814 *
815 * @return The authorization DN for this operation, or the null DN
816 * if it should be the unauthenticated user..
817 */
818 public final DN getAuthorizationDN()
819 {
820 if (authorizationEntry == null)
821 {
822 return DN.nullDN();
823 }
824 else
825 {
826 return authorizationEntry.getDN();
827 }
828 }
829
830
831
832 /**
833 * Retrieves the set of attachments defined for this operation, as a
834 * mapping between the attachment name and the associated object.
835 *
836 * @return The set of attachments defined for this operation.
837 */
838 public final Map<String,Object> getAttachments()
839 {
840 return attachments;
841 }
842
843
844
845 /**
846 * Set the attachments to the operation.
847 *
848 * @param attachments - Attachments to register within the
849 * operation
850 */
851 public final void setAttachments(Map<String, Object> attachments)
852 {
853 this.attachments = attachments;
854 }
855
856
857
858 /**
859 * Retrieves the attachment with the specified name.
860 *
861 * @param name The name for the attachment to retrieve. It will
862 * be treated in a case-sensitive manner.
863 *
864 * @return The requested attachment object, or {@code null} if it
865 * does not exist.
866 */
867 public final Object getAttachment(String name)
868 {
869 return attachments.get(name);
870 }
871
872
873
874 /**
875 * Removes the attachment with the specified name.
876 *
877 * @param name The name for the attachment to remove. It will be
878 * treated in a case-sensitive manner.
879 *
880 * @return The attachment that was removed, or {@code null} if it
881 * does not exist.
882 */
883 public final Object removeAttachment(String name)
884 {
885 return attachments.remove(name);
886 }
887
888
889
890 /**
891 * Sets the value of the specified attachment. If an attachment
892 * already exists with the same name, it will be replaced.
893 * Otherwise, a new attachment will be added.
894 *
895 * @param name The name to use for the attachment.
896 * @param value The value to use for the attachment.
897 *
898 * @return The former value held by the attachment with the given
899 * name, or {@code null} if there was previously no such
900 * attachment.
901 */
902 public final Object setAttachment(String name, Object value)
903 {
904 return attachments.put(name, value);
905 }
906
907
908
909 /**
910 * Indicates that processing on this operation has completed
911 * successfully and that the client should perform any associated
912 * cleanup work.
913 */
914 public final void operationCompleted()
915 {
916 // Notify the client connection that this operation is complete
917 // and that it no longer needs to be retained.
918 clientConnection.removeOperationInProgress(messageID);
919 }
920
921
922
923 /**
924 * Attempts to cancel this operation before processing has
925 * completed.
926 *
927 * @param cancelRequest Information about the way in which the
928 * operation should be canceled.
929 *
930 * @return A code providing information on the result of the
931 * cancellation.
932 */
933 public CancelResult cancel(CancelRequest cancelRequest)
934 {
935 abort(cancelRequest);
936
937 long stopWaitingTime = System.currentTimeMillis() + 5000;
938 while ((cancelResult == null) &&
939 (System.currentTimeMillis() < stopWaitingTime))
940 {
941 try
942 {
943 Thread.sleep(50);
944 }
945 catch (Exception e)
946 {
947 if (debugEnabled())
948 {
949 TRACER.debugCaught(DebugLogLevel.ERROR, e);
950 }
951 }
952 }
953
954 if (cancelResult == null)
955 {
956 // This can happen in some rare cases (e.g., if a client
957 // disconnects and there is still a lot of data to send to
958 // that client), and in this case we'll prevent the cancel
959 // thread from blocking for a long period of time.
960 cancelResult = new CancelResult(ResultCode.CANNOT_CANCEL, null);
961 }
962
963 return cancelResult;
964 }
965
966
967
968 /**
969 * Attempts to cancel this operation before processing has
970 * completed without waiting for a cancel result.
971 *
972 * @param cancelRequest Information about the way in which the
973 * operation should be canceled.
974 */
975 public synchronized void abort(CancelRequest cancelRequest)
976 {
977 if(cancelResult == null && this.cancelRequest == null)
978 {
979 this.cancelRequest = cancelRequest;
980 }
981 }
982
983
984
985 /**
986 * {@inheritDoc}
987 */
988 public synchronized final void
989 checkIfCanceled(boolean signalTooLate)
990 throws CanceledOperationException {
991 if(cancelRequest != null)
992 {
993 throw new CanceledOperationException(cancelRequest);
994 }
995
996 if(signalTooLate && cancelResult != null)
997 {
998 cancelResult = new CancelResult(ResultCode.TOO_LATE, null);
999 }
1000 }
1001
1002
1003
1004 /**
1005 * {@inheritDoc}
1006 */
1007 public final CancelRequest getCancelRequest()
1008 {
1009 return cancelRequest;
1010 }
1011
1012 /**
1013 * {@inheritDoc}
1014 */
1015 public final CancelResult getCancelResult()
1016 {
1017 return cancelResult;
1018 }
1019
1020
1021
1022 /**
1023 * Retrieves a string representation of this operation.
1024 *
1025 * @return A string representation of this operation.
1026 */
1027 public final String toString()
1028 {
1029 StringBuilder buffer = new StringBuilder();
1030 toString(buffer);
1031 return buffer.toString();
1032 }
1033
1034
1035
1036 /**
1037 * Appends a string representation of this operation to the provided
1038 * buffer.
1039 *
1040 * @param buffer The buffer into which a string representation of
1041 * this operation should be appended.
1042 */
1043 public abstract void toString(StringBuilder buffer);
1044
1045
1046
1047 /**
1048 * Retrieves the time that processing started for this operation.
1049 *
1050 * @return The time that processing started for this operation.
1051 */
1052 public final long getProcessingStartTime()
1053 {
1054 return processingStartTime;
1055 }
1056
1057
1058
1059 /**
1060 * Set the time at which the processing started for this operation.
1061 */
1062 public final void setProcessingStartTime()
1063 {
1064 processingStartTime = System.currentTimeMillis();
1065 if(useNanoTime)
1066 {
1067 processingStartNanoTime = System.nanoTime();
1068 }
1069 }
1070
1071
1072
1073 /**
1074 * Retrieves the time that processing stopped for this operation.
1075 * This will actually hold a time immediately before the response
1076 * was sent to the client.
1077 *
1078 * @return The time that processing stopped for this operation.
1079 */
1080 public final long getProcessingStopTime()
1081 {
1082 return processingStopTime;
1083 }
1084
1085
1086
1087 /**
1088 * Set the time at which the processing stopped for this operation.
1089 * This will actually hold a time immediately before the response
1090 * was sent to the client.
1091 */
1092 public final void setProcessingStopTime()
1093 {
1094 this.processingStopTime = System.currentTimeMillis();
1095 if(useNanoTime)
1096 {
1097 this.processingStopNanoTime = System.nanoTime();
1098 }
1099 }
1100
1101
1102
1103 /**
1104 * Retrieves the length of time in milliseconds that
1105 * the server spent processing this operation. This should not be
1106 * called until after the server has sent the response to the
1107 * client.
1108 *
1109 * @return The length of time in milliseconds that
1110 * the server spent processing this operation.
1111 */
1112 public final long getProcessingTime()
1113 {
1114 return (processingStopTime - processingStartTime);
1115 }
1116
1117
1118
1119 /**
1120 * Retrieves the length of time in nanoseconds that
1121 * the server spent processing this operation if available.
1122 * This should not be called until after the server has sent the
1123 * response to the client.
1124 *
1125 * @return The length of time in nanoseconds that the server
1126 * spent processing this operation or -1 if its not
1127 * available.
1128 */
1129 public final long getProcessingNanoTime()
1130 {
1131 if(useNanoTime)
1132 {
1133 return (processingStopNanoTime - processingStartNanoTime);
1134 }
1135 else
1136 {
1137 return -1;
1138 }
1139 }
1140
1141
1142
1143 /**
1144 * Performs the work of actually processing this operation. This
1145 * should include all processing for the operation, including
1146 * invoking pre-parse and post-response plugins, logging messages
1147 * and any other work that might need to be done in the course of
1148 * processing.
1149 */
1150 public abstract void run();
1151 }
1152