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    }