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.backends;
028    
029    
030    
031    import java.util.ArrayList;
032    import java.util.HashMap;
033    import java.util.HashSet;
034    import java.util.LinkedHashMap;
035    import java.util.LinkedHashSet;
036    import java.util.List;
037    
038    import org.opends.messages.Message;
039    import org.opends.server.admin.Configuration;
040    import org.opends.server.admin.server.ConfigurationChangeListener;
041    import org.opends.server.admin.std.server.MonitorBackendCfg;
042    import org.opends.server.admin.std.server.MonitorProviderCfg;
043    import org.opends.server.api.Backend;
044    import org.opends.server.api.MonitorProvider;
045    import org.opends.server.config.ConfigEntry;
046    import org.opends.server.config.ConfigException;
047    import org.opends.server.core.AddOperation;
048    import org.opends.server.core.DeleteOperation;
049    import org.opends.server.core.DirectoryServer;
050    import org.opends.server.core.ModifyOperation;
051    import org.opends.server.core.ModifyDNOperation;
052    import org.opends.server.core.SearchOperation;
053    import org.opends.server.loggers.debug.DebugTracer;
054    import org.opends.server.protocols.asn1.ASN1OctetString;
055    import org.opends.server.types.Attribute;
056    import org.opends.server.types.AttributeType;
057    import org.opends.server.types.AttributeValue;
058    import org.opends.server.types.BackupConfig;
059    import org.opends.server.types.BackupDirectory;
060    import org.opends.server.types.ConditionResult;
061    import org.opends.server.types.ConfigChangeResult;
062    import org.opends.server.types.DebugLogLevel;
063    import org.opends.server.types.DirectoryException;
064    import org.opends.server.types.DN;
065    import org.opends.server.types.Entry;
066    import org.opends.server.types.IndexType;
067    import org.opends.server.types.InitializationException;
068    import org.opends.server.types.LDIFExportConfig;
069    import org.opends.server.types.LDIFImportConfig;
070    import org.opends.server.types.LDIFImportResult;
071    import org.opends.server.types.ObjectClass;
072    import org.opends.server.types.RDN;
073    import org.opends.server.types.RestoreConfig;
074    import org.opends.server.types.ResultCode;
075    import org.opends.server.types.SearchFilter;
076    import org.opends.server.types.SearchScope;
077    import org.opends.server.util.DynamicConstants;
078    import org.opends.server.util.LDIFWriter;
079    import org.opends.server.util.TimeThread;
080    import org.opends.server.util.Validator;
081    
082    import static org.opends.server.config.ConfigConstants.*;
083    import static org.opends.server.loggers.debug.DebugLogger.*;
084    import static org.opends.messages.BackendMessages.*;
085    import static org.opends.messages.ConfigMessages.*;
086    import static org.opends.server.util.ServerConstants.*;
087    import static org.opends.server.util.StaticUtils.*;
088    
089    
090    
091    /**
092     * This class defines a backend to hold Directory Server monitor entries.  It
093     * will not actually store anything, but upon request will retrieve the
094     * requested monitor and dynamically generate the associated entry.  It will
095     * also construct a base monitor entry with some useful server-wide data.
096     */
097    public class MonitorBackend
098           extends Backend
099           implements ConfigurationChangeListener<MonitorBackendCfg>
100    {
101      /**
102       * The tracer object for the debug logger.
103       */
104      private static final DebugTracer TRACER = getTracer();
105    
106    
107    
108      // The set of user-defined attributes that will be included in the base
109      // monitor entry.
110      private ArrayList<Attribute> userDefinedAttributes;
111    
112      // The set of objectclasses that will be used in monitor entries.
113      private HashMap<ObjectClass,String> monitorObjectClasses;
114    
115      // The DN of the configuration entry for this backend.
116      private DN configEntryDN;
117    
118      // The current configuration state.
119      private MonitorBackendCfg currentConfig;
120    
121      // The DN for the base monitor entry.
122      private DN baseMonitorDN;
123    
124      // The set of base DNs for this backend.
125      private DN[] baseDNs;
126    
127      // The set of supported controls for this backend.
128      private HashSet<String> supportedControls;
129    
130      // The set of supported features for this backend.
131      private HashSet<String> supportedFeatures;
132    
133    
134    
135      /**
136       * Creates a new backend with the provided information.  All backend
137       * implementations must implement a default constructor that use
138       * <CODE>super()</CODE> to invoke this constructor.
139       */
140      public MonitorBackend()
141      {
142        super();
143    
144        // Perform all initialization in initializeBackend.
145      }
146    
147    
148      /**
149       * {@inheritDoc}
150       */
151      @Override()
152      public void configureBackend(Configuration config)
153             throws ConfigException
154      {
155        Validator.ensureNotNull(config);
156        Validator.ensureTrue(config instanceof MonitorBackendCfg);
157    
158        MonitorBackendCfg cfg = (MonitorBackendCfg)config;
159        ConfigEntry configEntry = DirectoryServer.getConfigEntry(cfg.dn());
160    
161    
162        // Make sure that a configuration entry was provided.  If not, then we will
163        // not be able to complete initialization.
164        if (configEntry == null)
165        {
166          Message message = ERR_MONITOR_CONFIG_ENTRY_NULL.get();
167          throw new ConfigException(message);
168        }
169    
170        configEntryDN = configEntry.getDN();
171    
172    
173        // Get the set of user-defined attributes for the configuration entry.  Any
174        // attributes that we don't recognize will be included directly in the base
175        // monitor entry.
176        userDefinedAttributes = new ArrayList<Attribute>();
177        for (List<Attribute> attrs :
178             configEntry.getEntry().getUserAttributes().values())
179        {
180          for (Attribute a : attrs)
181          {
182            if (! isMonitorConfigAttribute(a))
183            {
184              userDefinedAttributes.add(a);
185            }
186          }
187        }
188        for (List<Attribute> attrs :
189             configEntry.getEntry().getOperationalAttributes().values())
190        {
191          for (Attribute a : attrs)
192          {
193            if (! isMonitorConfigAttribute(a))
194            {
195              userDefinedAttributes.add(a);
196            }
197          }
198        }
199    
200    
201        // Construct the set of objectclasses to include in the base monitor entry.
202        monitorObjectClasses = new LinkedHashMap<ObjectClass,String>(2);
203        ObjectClass topOC = DirectoryServer.getObjectClass(OC_TOP, true);
204        monitorObjectClasses.put(topOC, OC_TOP);
205    
206        ObjectClass monitorOC = DirectoryServer.getObjectClass(OC_MONITOR_ENTRY,
207                                                               true);
208        monitorObjectClasses.put(monitorOC, OC_MONITOR_ENTRY);
209    
210    
211        // Define an empty sets for the supported controls and features.
212        supportedControls = new HashSet<String>(0);
213        supportedFeatures = new HashSet<String>(0);
214    
215        // Create the set of base DNs that we will handle.  In this case, it's just
216        // the DN of the base monitor entry.
217        try
218        {
219          baseMonitorDN = DN.decode(DN_MONITOR_ROOT);
220        }
221        catch (Exception e)
222        {
223          if (debugEnabled())
224          {
225            TRACER.debugCaught(DebugLogLevel.ERROR, e);
226          }
227    
228          Message message =
229              ERR_MONITOR_CANNOT_DECODE_MONITOR_ROOT_DN.get(getExceptionMessage(e));
230          throw new ConfigException(message, e);
231        }
232    
233        // FIXME -- Deal with this more correctly.
234        this.baseDNs = new DN[] { baseMonitorDN };
235    
236    
237        currentConfig = cfg;
238      }
239    
240    
241    
242      /**
243       * {@inheritDoc}
244       */
245      @Override()
246      public void initializeBackend()
247             throws ConfigException, InitializationException
248      {
249        // Register with the Directory Server as a configurable component.
250        currentConfig.addMonitorChangeListener(this);
251    
252    
253        // Register the monitor base as a private suffix.
254        try
255        {
256          DirectoryServer.registerBaseDN(baseMonitorDN, this, true);
257        }
258        catch (Exception e)
259        {
260          if (debugEnabled())
261          {
262            TRACER.debugCaught(DebugLogLevel.ERROR, e);
263          }
264    
265          Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
266              baseMonitorDN.toString(), getExceptionMessage(e));
267          throw new InitializationException(message, e);
268        }
269      }
270    
271    
272    
273      /**
274       * {@inheritDoc}
275       */
276      @Override()
277      public void finalizeBackend()
278      {
279        currentConfig.removeMonitorChangeListener(this);
280    
281        try
282        {
283          DirectoryServer.deregisterBaseDN(baseMonitorDN);
284        }
285        catch (Exception e)
286        {
287          if (debugEnabled())
288          {
289            TRACER.debugCaught(DebugLogLevel.ERROR, e);
290          }
291        }
292      }
293    
294    
295    
296      /**
297       * Indicates whether the provided attribute is one that is used in the
298       * configuration of this backend.
299       *
300       * @param  attribute  The attribute for which to make the determination.
301       *
302       * @return  <CODE>true</CODE> if the provided attribute is one that is used in
303       *          the configuration of this backend, <CODE>false</CODE> if not.
304       */
305      private boolean isMonitorConfigAttribute(Attribute attribute)
306      {
307        AttributeType attrType = attribute.getAttributeType();
308        if (attrType.hasName(ATTR_COMMON_NAME) ||
309            attrType.hasName(ATTR_BACKEND_ENABLED.toLowerCase()) ||
310            attrType.hasName(ATTR_BACKEND_CLASS.toLowerCase()) ||
311            attrType.hasName(ATTR_BACKEND_BASE_DN.toLowerCase()) ||
312            attrType.hasName(ATTR_BACKEND_ID.toLowerCase()) ||
313            attrType.hasName(ATTR_BACKEND_WRITABILITY_MODE.toLowerCase()))
314        {
315          return true;
316        }
317    
318        return false;
319      }
320    
321    
322    
323      /**
324       * {@inheritDoc}
325       */
326      @Override()
327      public DN[] getBaseDNs()
328      {
329        return baseDNs;
330      }
331    
332    
333    
334      /**
335       * {@inheritDoc}
336       */
337      @Override()
338      public long getEntryCount()
339      {
340        return DirectoryServer.getMonitorProviders().size() + 1;
341      }
342    
343    
344    
345      /**
346       * {@inheritDoc}
347       */
348      @Override()
349      public boolean isLocal()
350      {
351        // For the purposes of this method, this is a local backend.
352        return true;
353      }
354    
355    
356    
357      /**
358       * {@inheritDoc}
359       */
360      @Override()
361      public boolean isIndexed(AttributeType attributeType, IndexType indexType)
362      {
363        // All searches in this backend will always be considered indexed.
364        return true;
365      }
366    
367    
368    
369      /**
370       * {@inheritDoc}
371       */
372      @Override()
373      public ConditionResult hasSubordinates(DN entryDN)
374             throws DirectoryException
375      {
376        long ret = numSubordinates(entryDN, false);
377        if(ret < 0)
378        {
379          return ConditionResult.UNDEFINED;
380        }
381        else if(ret == 0)
382        {
383          return ConditionResult.FALSE;
384        }
385        else
386        {
387          return ConditionResult.TRUE;
388        }
389      }
390    
391    
392    
393      /**
394       * {@inheritDoc}
395       */
396      @Override()
397      public long numSubordinates(DN entryDN, boolean subtree)
398             throws DirectoryException
399      {
400        // If the requested entry was null, then return undefined.
401        if (entryDN == null)
402        {
403          return -1;
404        }
405    
406    
407        // If the requested entry was the monitor base entry, then return
408        // the number of monitor providers.
409        if (entryDN.equals(baseMonitorDN))
410        {
411          // This backend is only 1 level deep so the count is the same for
412          // subtree and immediate subordinates.
413          return DirectoryServer.getMonitorProviders().size();
414        }
415    
416    
417        // See if the monitor base entry is the immediate parent for the requested
418        // entry.  If not, then its undefined.
419        DN parentDN = entryDN.getParentDNInSuffix();
420        if ((parentDN == null) || (! parentDN.equals(baseMonitorDN)))
421        {
422          return -1;
423        }
424    
425    
426        // Get the RDN for the requested DN and make sure it is single-valued.
427        RDN entryRDN = entryDN.getRDN();
428        if (entryRDN.isMultiValued())
429        {
430          return -1;
431        }
432    
433    
434        // Get the RDN value and see if it matches the instance name for one of
435        // the directory server monitor providers.
436        String rdnValue = entryRDN.getAttributeValue(0).getStringValue();
437        MonitorProvider<? extends MonitorProviderCfg> monitorProvider =
438             DirectoryServer.getMonitorProvider(rdnValue.toLowerCase());
439        if (monitorProvider == null)
440        {
441          return -1;
442        }
443    
444        return 0;
445      }
446    
447    
448    
449      /**
450       * {@inheritDoc}
451       */
452      @Override()
453      public Entry getEntry(DN entryDN)
454             throws DirectoryException
455      {
456        // If the requested entry was null, then throw an exception.
457        if (entryDN == null)
458        {
459          Message message = ERR_MONITOR_GET_ENTRY_NULL.get();
460          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
461                                       message);
462        }
463    
464    
465        // If the requested entry was the monitor base entry, then retrieve it.
466        if (entryDN.equals(baseMonitorDN))
467        {
468          return getBaseMonitorEntry();
469        }
470    
471    
472        // See if the monitor base entry is the immediate parent for the requested
473        // entry.  If not, then throw an exception.
474        DN parentDN = entryDN.getParentDNInSuffix();
475        if ((parentDN == null) || (! parentDN.equals(baseMonitorDN)))
476        {
477          if (baseMonitorDN.isAncestorOf(entryDN))
478          {
479            Message message = ERR_MONITOR_BASE_TOO_DEEP.get(
480                String.valueOf(entryDN), String.valueOf(baseMonitorDN));
481            throw new DirectoryException(
482                    ResultCode.NO_SUCH_OBJECT, message, baseMonitorDN, null);
483          }
484          else
485          {
486            Message message = ERR_MONITOR_INVALID_BASE.get(
487                String.valueOf(entryDN), String.valueOf(baseMonitorDN));
488            throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
489          }
490        }
491    
492    
493        // Get the RDN for the requested DN and make sure it is single-valued.
494        RDN entryRDN = entryDN.getRDN();
495        if (entryRDN.isMultiValued())
496        {
497          Message message =
498              ERR_MONITOR_MULTIVALUED_RDN.get(String.valueOf(entryDN));
499          throw new DirectoryException(
500                  ResultCode.NO_SUCH_OBJECT, message, baseMonitorDN, null);
501        }
502    
503    
504        // Get the RDN value and see if it matches the instance name for one of
505        // the directory server monitor providers.
506        String rdnValue = entryRDN.getAttributeValue(0).getStringValue();
507        MonitorProvider<? extends MonitorProviderCfg> monitorProvider =
508             DirectoryServer.getMonitorProvider(rdnValue.toLowerCase());
509        if (monitorProvider == null)
510        {
511          Message message =
512              ERR_MONITOR_NO_SUCH_PROVIDER.get(String.valueOf(rdnValue));
513          throw new DirectoryException(
514                  ResultCode.NO_SUCH_OBJECT, message, baseMonitorDN, null);
515        }
516    
517    
518        // Take the data from the monitor provider and stuff it into an entry.
519        return getMonitorEntry(entryDN, monitorProvider);
520      }
521    
522    
523    
524      /**
525       * {@inheritDoc}
526       */
527      @Override()
528      public boolean entryExists(DN entryDN)
529             throws DirectoryException
530      {
531        if (entryDN.equals(baseMonitorDN))
532        {
533          return true;
534        }
535    
536        DN parentDN = entryDN.getParentDNInSuffix();
537        if ((parentDN == null) || (! parentDN.equals(baseMonitorDN)))
538        {
539          return false;
540        }
541    
542        RDN rdn = entryDN.getRDN();
543        if (rdn.isMultiValued())
544        {
545          return false;
546        }
547    
548        String rdnValue = rdn.getAttributeValue(0).getStringValue();
549        MonitorProvider monitorProvider =
550             DirectoryServer.getMonitorProvider(toLowerCase(rdnValue));
551        return (monitorProvider != null);
552      }
553    
554    
555    
556      /**
557       * Retrieves the base monitor entry for the Directory Server.
558       *
559       * @return  The base monitor entry for the Directory Server.
560       */
561      public Entry getBaseMonitorEntry()
562      {
563        HashMap<ObjectClass,String> monitorClasses =
564             new LinkedHashMap<ObjectClass,String>(3);
565        monitorClasses.putAll(monitorObjectClasses);
566    
567        ObjectClass extensibleObjectOC =
568             DirectoryServer.getObjectClass(OC_EXTENSIBLE_OBJECT_LC, true);
569        monitorClasses.put(extensibleObjectOC, OC_EXTENSIBLE_OBJECT);
570    
571        HashMap<AttributeType,List<Attribute>> monitorUserAttrs =
572             new LinkedHashMap<AttributeType,List<Attribute>>();
573    
574        HashMap<AttributeType,List<Attribute>> monitorOperationalAttrs =
575             new LinkedHashMap<AttributeType,List<Attribute>>();
576    
577    
578        // Add the "cn" attribute.
579        Attribute cnAttr = createAttribute(ATTR_COMMON_NAME, ATTR_COMMON_NAME,
580                                           "monitor");
581        ArrayList<Attribute> cnList = new ArrayList<Attribute>(1);
582        cnList.add(cnAttr);
583        monitorUserAttrs.put(cnAttr.getAttributeType(), cnList);
584    
585    
586        // Add the server product name.
587        Attribute productNameAttr = createAttribute(ATTR_PRODUCT_NAME,
588                                                    ATTR_PRODUCT_NAME_LC,
589                                                    DynamicConstants.PRODUCT_NAME);
590        ArrayList<Attribute> productNameList = new ArrayList<Attribute>(1);
591        productNameList.add(productNameAttr);
592        monitorUserAttrs.put(productNameAttr.getAttributeType(), productNameList);
593    
594    
595        // Add the vendor name.
596        Attribute vendorNameAttr = createAttribute(ATTR_VENDOR_NAME,
597                                                   ATTR_VENDOR_NAME_LC,
598                                                   SERVER_VENDOR_NAME);
599        ArrayList<Attribute> vendorNameList = new ArrayList<Attribute>(1);
600        vendorNameList.add(vendorNameAttr);
601        monitorUserAttrs.put(vendorNameAttr.getAttributeType(), vendorNameList);
602    
603    
604        // Add the vendor version.
605        Attribute versionAttr = createAttribute(ATTR_VENDOR_VERSION,
606                                                ATTR_VENDOR_VERSION_LC,
607                                                DirectoryServer.getVersionString());
608        ArrayList<Attribute> versionList = new ArrayList<Attribute>(1);
609        versionList.add(versionAttr);
610        monitorUserAttrs.put(versionAttr.getAttributeType(), versionList);
611    
612    
613        // Add the server startup time.
614        Attribute startTimeAttr =
615             createAttribute(ATTR_START_TIME, ATTR_START_TIME_LC,
616                             DirectoryServer.getStartTimeUTC());
617        ArrayList<Attribute> startTimeList = new ArrayList<Attribute>(1);
618        startTimeList.add(startTimeAttr);
619        monitorUserAttrs.put(startTimeAttr.getAttributeType(), startTimeList);
620    
621    
622        // Add the current time.
623        Attribute currentTimeAttr =
624             createAttribute(ATTR_CURRENT_TIME, ATTR_CURRENT_TIME_LC,
625                             TimeThread.getGMTTime());
626        ArrayList<Attribute> currentTimeList = new ArrayList<Attribute>(1);
627        currentTimeList.add(currentTimeAttr);
628        monitorUserAttrs.put(currentTimeAttr.getAttributeType(), currentTimeList);
629    
630    
631        // Add the uptime as a human-readable string.
632        long upSeconds =
633             ((System.currentTimeMillis() - DirectoryServer.getStartTime()) / 1000);
634        long upDays = (upSeconds / 86400);
635        upSeconds %= 86400;
636        long upHours = (upSeconds / 3600);
637        upSeconds %= 3600;
638        long upMinutes = (upSeconds / 60);
639        upSeconds %= 60;
640        Message upTimeStr =
641            INFO_MONITOR_UPTIME.get(upDays, upHours, upMinutes, upSeconds);
642        Attribute upTimeAttr = createAttribute(ATTR_UP_TIME, ATTR_UP_TIME_LC,
643                                               upTimeStr.toString());
644        ArrayList<Attribute> upTimeList = new ArrayList<Attribute>(1);
645        upTimeList.add(upTimeAttr);
646        monitorUserAttrs.put(upTimeAttr.getAttributeType(), upTimeList);
647    
648    
649        // Add the number of connections currently established.
650        long currentConns = DirectoryServer.getCurrentConnections();
651        Attribute currentConnsAttr = createAttribute(ATTR_CURRENT_CONNS,
652                                                     ATTR_CURRENT_CONNS_LC,
653                                                     String.valueOf(currentConns));
654        ArrayList<Attribute> currentConnsList = new ArrayList<Attribute>(1);
655        currentConnsList.add(currentConnsAttr);
656        monitorUserAttrs.put(currentConnsAttr.getAttributeType(), currentConnsList);
657    
658    
659        // Add the maximum number of connections established at one time.
660        long maxConns = DirectoryServer.getMaxConnections();
661        Attribute maxConnsAttr = createAttribute(ATTR_MAX_CONNS,
662                                                 ATTR_MAX_CONNS_LC,
663                                                 String.valueOf(maxConns));
664        ArrayList<Attribute> maxConnsList = new ArrayList<Attribute>(1);
665        maxConnsList.add(maxConnsAttr);
666        monitorUserAttrs.put(maxConnsAttr.getAttributeType(), maxConnsList);
667    
668    
669        // Add the total number of connections the server has accepted.
670        long totalConns = DirectoryServer.getTotalConnections();
671        Attribute totalConnsAttr = createAttribute(ATTR_TOTAL_CONNS,
672                                                   ATTR_TOTAL_CONNS_LC,
673                                                   String.valueOf(totalConns));
674        ArrayList<Attribute> totalConnsList = new ArrayList<Attribute>(1);
675        totalConnsList.add(totalConnsAttr);
676        monitorUserAttrs.put(totalConnsAttr.getAttributeType(), totalConnsList);
677    
678    
679        // Add all the user-defined attributes.
680        for (Attribute a : userDefinedAttributes)
681        {
682          AttributeType type = a.getAttributeType();
683    
684          if (type.isOperational())
685          {
686            List<Attribute> attrs = monitorOperationalAttrs.get(type);
687            if (attrs == null)
688            {
689              attrs = new ArrayList<Attribute>();
690              attrs.add(a);
691              monitorOperationalAttrs.put(type, attrs);
692            }
693            else
694            {
695              attrs.add(a);
696            }
697          }
698          else
699          {
700            List<Attribute> attrs = monitorUserAttrs.get(type);
701            if (attrs == null)
702            {
703              attrs = new ArrayList<Attribute>();
704              attrs.add(a);
705              monitorUserAttrs.put(type, attrs);
706            }
707            else
708            {
709              attrs.add(a);
710            }
711          }
712        }
713    
714    
715        // Construct and return the entry.
716        Entry e = new Entry(baseMonitorDN, monitorClasses, monitorUserAttrs,
717                            monitorOperationalAttrs);
718        e.processVirtualAttributes();
719        return e;
720      }
721    
722    
723    
724      /**
725       * Generates and returns a monitor entry based on the contents of the
726       * provided monitor provider.
727       *
728       * @param  entryDN          The DN to use for the entry.
729       * @param  monitorProvider  The monitor provider to use to obtain the
730       *                          information for the entry.
731       *
732       * @return  The monitor entry generated from the information in the provided
733       *          monitor provider.
734       */
735      private Entry getMonitorEntry(DN entryDN,
736                         MonitorProvider<? extends MonitorProviderCfg>
737                              monitorProvider)
738      {
739        HashMap<ObjectClass,String> monitorClasses =
740             new LinkedHashMap<ObjectClass,String>(3);
741        monitorClasses.putAll(monitorObjectClasses);
742    
743        ObjectClass monitorOC = monitorProvider.getMonitorObjectClass();
744        monitorClasses.put(monitorOC, monitorOC.getPrimaryName());
745    
746        List<Attribute> monitorAttrs = monitorProvider.getMonitorData();
747        HashMap<AttributeType,List<Attribute>> attrMap =
748             new LinkedHashMap<AttributeType,List<Attribute>>(
749                      monitorAttrs.size()+1);
750    
751    
752        // Make sure to include the RDN attribute.
753        RDN            entryRDN = entryDN.getRDN();
754        AttributeType  rdnType  = entryRDN.getAttributeType(0);
755        AttributeValue rdnValue = entryRDN.getAttributeValue(0);
756    
757        LinkedHashSet<AttributeValue> rdnValues =
758             new LinkedHashSet<AttributeValue>(1);
759        rdnValues.add(rdnValue);
760    
761        Attribute rdnAttr = new Attribute(rdnType, entryRDN.getAttributeName(0),
762                                          rdnValues);
763        ArrayList<Attribute> rdnList = new ArrayList<Attribute>(1);
764        rdnList.add(rdnAttr);
765        attrMap.put(rdnType, rdnList);
766    
767    
768        // Take the rest of the information from the monitor data.
769        for (Attribute a : monitorAttrs)
770        {
771          AttributeType type = a.getAttributeType();
772    
773          List<Attribute> attrs = attrMap.get(type);
774          if (attrs == null)
775          {
776            attrs = new ArrayList<Attribute>();
777            attrs.add(a);
778            attrMap.put(type, attrs);
779          }
780          else
781          {
782            attrs.add(a);
783          }
784        }
785    
786        Entry e = new Entry(entryDN, monitorClasses, attrMap,
787                            new HashMap<AttributeType,List<Attribute>>(0));
788        e.processVirtualAttributes();
789        return e;
790      }
791    
792    
793    
794      /**
795       * Creates an attribute for a monitor entry with the following criteria.
796       *
797       * @param  name       The name for the attribute.
798       * @param  lowerName  The name for the attribute formatted in all lowercase
799       *                    characters.
800       * @param  value      The value to use for the attribute.
801       *
802       * @return  The constructed attribute.
803       */
804      private Attribute createAttribute(String name, String lowerName,
805                                        String value)
806      {
807        AttributeType type = DirectoryServer.getAttributeType(lowerName);
808        if (type == null)
809        {
810          type = DirectoryServer.getDefaultAttributeType(name);
811        }
812    
813        LinkedHashSet<AttributeValue> attrValues =
814             new LinkedHashSet<AttributeValue>(1);
815        attrValues.add(new AttributeValue(type, new ASN1OctetString(value)));
816    
817        return new Attribute(type, name, attrValues);
818      }
819    
820    
821    
822      /**
823       * {@inheritDoc}
824       */
825      @Override()
826      public void addEntry(Entry entry, AddOperation addOperation)
827             throws DirectoryException
828      {
829        Message message =
830            ERR_MONITOR_ADD_NOT_SUPPORTED.get(String.valueOf(entry.getDN()));
831        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
832      }
833    
834    
835    
836      /**
837       * {@inheritDoc}
838       */
839      @Override()
840      public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
841             throws DirectoryException
842      {
843        Message message =
844            ERR_MONITOR_DELETE_NOT_SUPPORTED.get(String.valueOf(entryDN));
845        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
846      }
847    
848    
849    
850      /**
851       * {@inheritDoc}
852       */
853      @Override()
854      public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
855             throws DirectoryException
856      {
857        Message message = ERR_MONITOR_MODIFY_NOT_SUPPORTED.get(
858            String.valueOf(entry.getDN()), String.valueOf(configEntryDN));
859        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
860      }
861    
862    
863    
864      /**
865       * {@inheritDoc}
866       */
867      @Override()
868      public void renameEntry(DN currentDN, Entry entry,
869                                       ModifyDNOperation modifyDNOperation)
870             throws DirectoryException
871      {
872        Message message =
873            ERR_MONITOR_MODIFY_DN_NOT_SUPPORTED.get(String.valueOf(currentDN));
874        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
875      }
876    
877    
878    
879      /**
880       * {@inheritDoc}
881       */
882      @Override()
883      public void search(SearchOperation searchOperation)
884             throws DirectoryException
885      {
886        // Get the base entry for the search, if possible.  If it doesn't exist,
887        // then this will throw an exception.
888        DN    baseDN    = searchOperation.getBaseDN();
889        Entry baseEntry = getEntry(baseDN);
890    
891    
892        // Figure out whether the base is the monitor base entry or one of its
893        // children for a specific monitor.
894        SearchScope  scope  = searchOperation.getScope();
895        SearchFilter filter = searchOperation.getFilter();
896        if (baseMonitorDN.equals(baseDN))
897        {
898          // If it is a base-level or subtree search, then we need to look at the
899          // base monitor entry.
900          if ((scope == SearchScope.BASE_OBJECT) ||
901              (scope == SearchScope.WHOLE_SUBTREE))
902          {
903            if (filter.matchesEntry(baseEntry))
904            {
905              searchOperation.returnEntry(baseEntry, null);
906            }
907    
908    
909            // If it is a base-level search, then we're done.
910            if (scope == SearchScope.BASE_OBJECT)
911            {
912              return;
913            }
914          }
915    
916    
917          // Iterate through all of the monitor providers defined in the server.
918          // Get an entry for each and compare it against the filter.
919          for (MonitorProvider<? extends MonitorProviderCfg> monitorProvider :
920               DirectoryServer.getMonitorProviders().values())
921          {
922            DN providerDN = DirectoryServer.getMonitorProviderDN(monitorProvider);
923            Entry monitorEntry = getMonitorEntry(providerDN, monitorProvider);
924            if (filter.matchesEntry(monitorEntry))
925            {
926              searchOperation.returnEntry(monitorEntry, null);
927            }
928          }
929        }
930        else
931        {
932          // Look at the scope for the search.  We only need to return something if
933          // it is a base-level or subtree search.
934          if ((scope == SearchScope.BASE_OBJECT) ||
935              (scope == SearchScope.WHOLE_SUBTREE))
936          {
937            if (filter.matchesEntry(baseEntry))
938            {
939              searchOperation.returnEntry(baseEntry, null);
940            }
941          }
942        }
943      }
944    
945    
946    
947      /**
948       * {@inheritDoc}
949       */
950      @Override()
951      public HashSet<String> getSupportedControls()
952      {
953        return supportedControls;
954      }
955    
956    
957    
958      /**
959       * {@inheritDoc}
960       */
961      @Override()
962      public HashSet<String> getSupportedFeatures()
963      {
964        return supportedFeatures;
965      }
966    
967    
968    
969      /**
970       * {@inheritDoc}
971       */
972      @Override()
973      public boolean supportsLDIFExport()
974      {
975        // We can export all the monitor entries as a point-in-time snapshot.
976        // TODO implementation of export is incomplete
977        // TODO export-ldif reports nonsense for upTime etc.
978        return false;
979      }
980    
981    
982    
983      /**
984       * {@inheritDoc}
985       */
986      @Override()
987      public void exportLDIF(LDIFExportConfig exportConfig)
988             throws DirectoryException
989      {
990        // TODO export-ldif reports nonsense for upTime etc.
991    
992        // Create the LDIF writer.
993        LDIFWriter ldifWriter;
994        try
995        {
996          ldifWriter = new LDIFWriter(exportConfig);
997        }
998        catch (Exception e)
999        {
1000          if (debugEnabled())
1001          {
1002            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1003          }
1004    
1005          Message message = ERR_ROOTDSE_UNABLE_TO_CREATE_LDIF_WRITER.get(
1006              stackTraceToSingleLineString(e));
1007          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1008                                       message);
1009        }
1010    
1011    
1012        // Write the base monitor entry to the LDIF.
1013        try
1014        {
1015          ldifWriter.writeEntry(getBaseMonitorEntry());
1016        }
1017        catch (Exception e)
1018        {
1019          if (debugEnabled())
1020          {
1021            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1022          }
1023    
1024          try
1025          {
1026            ldifWriter.close();
1027          }
1028          catch (Exception e2)
1029          {
1030            if (debugEnabled())
1031            {
1032              TRACER.debugCaught(DebugLogLevel.ERROR, e2);
1033            }
1034          }
1035    
1036          Message message = ERR_MONITOR_UNABLE_TO_EXPORT_BASE.get(
1037              stackTraceToSingleLineString(e));
1038          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1039                                       message);
1040        }
1041    
1042    
1043        // Get all the monitor providers, convert them to entries, and write them to
1044        // LDIF.
1045        for (MonitorProvider monitorProvider :
1046             DirectoryServer.getMonitorProviders().values())
1047        {
1048          try
1049          {
1050            // TODO implementation of export is incomplete
1051          }
1052          catch (Exception e)
1053          {
1054            if (debugEnabled())
1055            {
1056              TRACER.debugCaught(DebugLogLevel.ERROR, e);
1057            }
1058    
1059            try
1060            {
1061              ldifWriter.close();
1062            }
1063            catch (Exception e2)
1064            {
1065              if (debugEnabled())
1066              {
1067                TRACER.debugCaught(DebugLogLevel.ERROR, e2);
1068              }
1069            }
1070    
1071            Message message = ERR_MONITOR_UNABLE_TO_EXPORT_PROVIDER_ENTRY.
1072                get(monitorProvider.getMonitorInstanceName(),
1073                    stackTraceToSingleLineString(e));
1074            throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1075                                         message);
1076          }
1077        }
1078    
1079    
1080        // Close the monitor provider and return.
1081        try
1082        {
1083          ldifWriter.close();
1084        }
1085        catch (Exception e)
1086        {
1087          if (debugEnabled())
1088          {
1089            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1090          }
1091        }
1092      }
1093    
1094    
1095    
1096      /**
1097       * {@inheritDoc}
1098       */
1099      @Override()
1100      public boolean supportsLDIFImport()
1101      {
1102        // This backend does not support LDIF imports.
1103        return false;
1104      }
1105    
1106    
1107    
1108      /**
1109       * {@inheritDoc}
1110       */
1111      @Override()
1112      public LDIFImportResult importLDIF(LDIFImportConfig importConfig)
1113             throws DirectoryException
1114      {
1115        // This backend does not support LDIF imports.
1116        Message message = ERR_MONITOR_IMPORT_NOT_SUPPORTED.get();
1117        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
1118      }
1119    
1120    
1121    
1122      /**
1123       * {@inheritDoc}
1124       */
1125      @Override()
1126      public boolean supportsBackup()
1127      {
1128        // This backend does not provide a backup/restore mechanism.
1129        return false;
1130      }
1131    
1132    
1133    
1134      /**
1135       * {@inheritDoc}
1136       */
1137      @Override()
1138      public boolean supportsBackup(BackupConfig backupConfig,
1139                                    StringBuilder unsupportedReason)
1140      {
1141        // This backend does not provide a backup/restore mechanism.
1142        return false;
1143      }
1144    
1145    
1146    
1147      /**
1148       * {@inheritDoc}
1149       */
1150      @Override()
1151      public void createBackup(BackupConfig backupConfig)
1152             throws DirectoryException
1153      {
1154        // This backend does not provide a backup/restore mechanism.
1155        Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
1156        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
1157      }
1158    
1159    
1160    
1161      /**
1162       * {@inheritDoc}
1163       */
1164      @Override()
1165      public void removeBackup(BackupDirectory backupDirectory,
1166                               String backupID)
1167             throws DirectoryException
1168      {
1169        // This backend does not provide a backup/restore mechanism.
1170        Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
1171        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
1172      }
1173    
1174    
1175    
1176      /**
1177       * {@inheritDoc}
1178       */
1179      @Override()
1180      public boolean supportsRestore()
1181      {
1182        // This backend does not provide a backup/restore mechanism.
1183        return false;
1184      }
1185    
1186    
1187    
1188      /**
1189       * {@inheritDoc}
1190       */
1191      @Override()
1192      public void restoreBackup(RestoreConfig restoreConfig)
1193             throws DirectoryException
1194      {
1195        // This backend does not provide a backup/restore mechanism.
1196        Message message = ERR_MONITOR_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
1197        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
1198      }
1199    
1200    
1201    
1202      /**
1203       * {@inheritDoc}
1204       */
1205      public boolean isConfigurationChangeAcceptable(
1206           MonitorBackendCfg backendCfg,
1207           List<Message> unacceptableReasons)
1208      {
1209        // We'll pretty much accept anything here as long as it isn't one of our
1210        // private attributes.
1211        return true;
1212      }
1213    
1214    
1215    
1216      /**
1217       * {@inheritDoc}
1218       */
1219      public ConfigChangeResult applyConfigurationChange(
1220                                     MonitorBackendCfg backendCfg)
1221      {
1222        ResultCode        resultCode          = ResultCode.SUCCESS;
1223        boolean           adminActionRequired = false;
1224        ArrayList<Message> messages            = new ArrayList<Message>();
1225    
1226    
1227        // Check to see if there is a new set of user-defined attributes.
1228        ArrayList<Attribute> userAttrs = new ArrayList<Attribute>();
1229        try
1230        {
1231          ConfigEntry configEntry = DirectoryServer.getConfigEntry(configEntryDN);
1232          for (List<Attribute> attrs :
1233               configEntry.getEntry().getUserAttributes().values())
1234          {
1235            for (Attribute a : attrs)
1236            {
1237              if (! isMonitorConfigAttribute(a))
1238              {
1239                userAttrs.add(a);
1240              }
1241            }
1242          }
1243          for (List<Attribute> attrs :
1244               configEntry.getEntry().getOperationalAttributes().values())
1245          {
1246            for (Attribute a : attrs)
1247            {
1248              if (! isMonitorConfigAttribute(a))
1249              {
1250                userAttrs.add(a);
1251              }
1252            }
1253          }
1254        }
1255        catch (Exception e)
1256        {
1257          if (debugEnabled())
1258          {
1259            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1260          }
1261    
1262    
1263          messages.add(ERR_CONFIG_BACKEND_ERROR_INTERACTING_WITH_BACKEND_ENTRY.get(
1264                  String.valueOf(configEntryDN),
1265                  stackTraceToSingleLineString(e)));
1266          resultCode = DirectoryServer.getServerErrorResultCode();
1267        }
1268    
1269    
1270        userDefinedAttributes = userAttrs;
1271    
1272        Message message = INFO_MONITOR_USING_NEW_USER_ATTRS.get();
1273        messages.add(message);
1274    
1275    
1276        currentConfig = backendCfg;
1277        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
1278      }
1279    
1280    
1281    
1282      /**
1283       * {@inheritDoc}
1284       */
1285      public void preloadEntryCache() throws UnsupportedOperationException {
1286        throw new UnsupportedOperationException("Operation not supported.");
1287      }
1288    }