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