View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.regionserver;
20  
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.io.StringWriter;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.concurrent.BlockingQueue;
28  import java.util.concurrent.Executors;
29  import java.util.concurrent.PriorityBlockingQueue;
30  import java.util.concurrent.RejectedExecutionException;
31  import java.util.concurrent.RejectedExecutionHandler;
32  import java.util.concurrent.ThreadFactory;
33  import java.util.concurrent.ThreadPoolExecutor;
34  import java.util.concurrent.TimeUnit;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.hadoop.hbase.classification.InterfaceAudience;
39  import org.apache.hadoop.hbase.conf.ConfigurationManager;
40  import org.apache.hadoop.hbase.conf.PropagatingConfigurationObserver;
41  import org.apache.hadoop.conf.Configuration;
42  import org.apache.hadoop.hbase.regionserver.compactions.CompactionContext;
43  import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest;
44  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
45  import org.apache.hadoop.hbase.util.Pair;
46  import org.apache.hadoop.ipc.RemoteException;
47  import org.apache.hadoop.util.StringUtils;
48  
49  import com.google.common.base.Preconditions;
50  
51  /**
52   * Compact region on request and then run split if appropriate
53   */
54  @InterfaceAudience.Private
55  public class CompactSplitThread implements CompactionRequestor, PropagatingConfigurationObserver {
56    static final Log LOG = LogFactory.getLog(CompactSplitThread.class);
57  
58    // Configuration key for the large compaction threads.
59    public final static String LARGE_COMPACTION_THREADS =
60        "hbase.regionserver.thread.compaction.large";
61    public final static int LARGE_COMPACTION_THREADS_DEFAULT = 1;
62    
63    // Configuration key for the small compaction threads.
64    public final static String SMALL_COMPACTION_THREADS =
65        "hbase.regionserver.thread.compaction.small";
66    public final static int SMALL_COMPACTION_THREADS_DEFAULT = 1;
67    
68    // Configuration key for split threads
69    public final static String SPLIT_THREADS = "hbase.regionserver.thread.split";
70    public final static int SPLIT_THREADS_DEFAULT = 1;
71    
72    // Configuration keys for merge threads
73    public final static String MERGE_THREADS = "hbase.regionserver.thread.merge";
74    public final static int MERGE_THREADS_DEFAULT = 1;
75    
76    private final HRegionServer server;
77    private final Configuration conf;
78  
79    private final ThreadPoolExecutor longCompactions;
80    private final ThreadPoolExecutor shortCompactions;
81    private final ThreadPoolExecutor splits;
82    private final ThreadPoolExecutor mergePool;
83  
84    /**
85     * Splitting should not take place if the total number of regions exceed this.
86     * This is not a hard limit to the number of regions but it is a guideline to
87     * stop splitting after number of online regions is greater than this.
88     */
89    private int regionSplitLimit;
90  
91    /** @param server */
92    CompactSplitThread(HRegionServer server) {
93      super();
94      this.server = server;
95      this.conf = server.getConfiguration();
96      this.regionSplitLimit = conf.getInt("hbase.regionserver.regionSplitLimit",
97          Integer.MAX_VALUE);
98  
99      int largeThreads = Math.max(1, conf.getInt(
100         LARGE_COMPACTION_THREADS, LARGE_COMPACTION_THREADS_DEFAULT));
101     int smallThreads = conf.getInt(
102         SMALL_COMPACTION_THREADS, SMALL_COMPACTION_THREADS_DEFAULT);
103 
104     int splitThreads = conf.getInt(SPLIT_THREADS, SPLIT_THREADS_DEFAULT);
105 
106     // if we have throttle threads, make sure the user also specified size
107     Preconditions.checkArgument(largeThreads > 0 && smallThreads > 0);
108 
109     final String n = Thread.currentThread().getName();
110 
111     this.longCompactions = new ThreadPoolExecutor(largeThreads, largeThreads,
112         60, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>(),
113         new ThreadFactory() {
114           @Override
115           public Thread newThread(Runnable r) {
116             Thread t = new Thread(r);
117             t.setName(n + "-longCompactions-" + System.currentTimeMillis());
118             return t;
119           }
120       });
121     this.longCompactions.setRejectedExecutionHandler(new Rejection());
122     this.shortCompactions = new ThreadPoolExecutor(smallThreads, smallThreads,
123         60, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>(),
124         new ThreadFactory() {
125           @Override
126           public Thread newThread(Runnable r) {
127             Thread t = new Thread(r);
128             t.setName(n + "-shortCompactions-" + System.currentTimeMillis());
129             return t;
130           }
131       });
132     this.shortCompactions
133         .setRejectedExecutionHandler(new Rejection());
134     this.splits = (ThreadPoolExecutor)
135         Executors.newFixedThreadPool(splitThreads,
136             new ThreadFactory() {
137           @Override
138           public Thread newThread(Runnable r) {
139             Thread t = new Thread(r);
140             t.setName(n + "-splits-" + System.currentTimeMillis());
141             return t;
142           }
143       });
144     int mergeThreads = conf.getInt(MERGE_THREADS, MERGE_THREADS_DEFAULT);
145     this.mergePool = (ThreadPoolExecutor) Executors.newFixedThreadPool(
146         mergeThreads, new ThreadFactory() {
147           @Override
148           public Thread newThread(Runnable r) {
149             Thread t = new Thread(r);
150             t.setName(n + "-merges-" + System.currentTimeMillis());
151             return t;
152           }
153         });
154   }
155 
156   @Override
157   public String toString() {
158     return "compaction_queue=("
159         + longCompactions.getQueue().size() + ":"
160         + shortCompactions.getQueue().size() + ")"
161         + ", split_queue=" + splits.getQueue().size()
162         + ", merge_queue=" + mergePool.getQueue().size();
163   }
164   
165   public String dumpQueue() {
166     StringBuffer queueLists = new StringBuffer();
167     queueLists.append("Compaction/Split Queue dump:\n");
168     queueLists.append("  LargeCompation Queue:\n");
169     BlockingQueue<Runnable> lq = longCompactions.getQueue();
170     Iterator<Runnable> it = lq.iterator();
171     while(it.hasNext()){
172       queueLists.append("    "+it.next().toString());
173       queueLists.append("\n");
174     }
175 
176     if( shortCompactions != null ){
177       queueLists.append("\n");
178       queueLists.append("  SmallCompation Queue:\n");
179       lq = shortCompactions.getQueue();
180       it = lq.iterator();
181       while(it.hasNext()){
182         queueLists.append("    "+it.next().toString());
183         queueLists.append("\n");
184       }
185     }
186     
187     queueLists.append("\n");
188     queueLists.append("  Split Queue:\n");
189     lq = splits.getQueue();
190     it = lq.iterator();
191     while(it.hasNext()){
192       queueLists.append("    "+it.next().toString());
193       queueLists.append("\n");
194     }
195     
196     queueLists.append("\n");
197     queueLists.append("  Region Merge Queue:\n");
198     lq = mergePool.getQueue();
199     it = lq.iterator();
200     while (it.hasNext()) {
201       queueLists.append("    " + it.next().toString());
202       queueLists.append("\n");
203     }
204 
205     return queueLists.toString();
206   }
207 
208   public synchronized void requestRegionsMerge(final HRegion a,
209       final HRegion b, final boolean forcible) {
210     try {
211       mergePool.execute(new RegionMergeRequest(a, b, this.server, forcible));
212       if (LOG.isDebugEnabled()) {
213         LOG.debug("Region merge requested for " + a + "," + b + ", forcible="
214             + forcible + ".  " + this);
215       }
216     } catch (RejectedExecutionException ree) {
217       LOG.warn("Could not execute merge for " + a + "," + b + ", forcible="
218           + forcible, ree);
219     }
220   }
221 
222   public synchronized boolean requestSplit(final HRegion r) {
223     // don't split regions that are blocking
224     if (shouldSplitRegion() && r.getCompactPriority() >= Store.PRIORITY_USER) {
225       byte[] midKey = r.checkSplit();
226       if (midKey != null) {
227         requestSplit(r, midKey);
228         return true;
229       }
230     }
231     return false;
232   }
233 
234   public synchronized void requestSplit(final HRegion r, byte[] midKey) {
235     if (midKey == null) {
236       LOG.debug("Region " + r.getRegionNameAsString() +
237         " not splittable because midkey=null");
238       if (r.shouldForceSplit()) {
239         r.clearSplit();
240       }
241       return;
242     }
243     try {
244       this.splits.execute(new SplitRequest(r, midKey, this.server));
245       if (LOG.isDebugEnabled()) {
246         LOG.debug("Split requested for " + r + ".  " + this);
247       }
248     } catch (RejectedExecutionException ree) {
249       LOG.info("Could not execute split for " + r, ree);
250     }
251   }
252 
253   @Override
254   public synchronized List<CompactionRequest> requestCompaction(final HRegion r, final String why)
255       throws IOException {
256     return requestCompaction(r, why, null);
257   }
258 
259   @Override
260   public synchronized List<CompactionRequest> requestCompaction(final HRegion r, final String why,
261       List<Pair<CompactionRequest, Store>> requests) throws IOException {
262     return requestCompaction(r, why, Store.NO_PRIORITY, requests);
263   }
264 
265   @Override
266   public synchronized CompactionRequest requestCompaction(final HRegion r, final Store s,
267       final String why, CompactionRequest request) throws IOException {
268     return requestCompaction(r, s, why, Store.NO_PRIORITY, request);
269   }
270 
271   @Override
272   public synchronized List<CompactionRequest> requestCompaction(final HRegion r, final String why,
273       int p, List<Pair<CompactionRequest, Store>> requests) throws IOException {
274     return requestCompactionInternal(r, why, p, requests, true);
275   }
276 
277   private List<CompactionRequest> requestCompactionInternal(final HRegion r, final String why,
278       int p, List<Pair<CompactionRequest, Store>> requests, boolean selectNow) throws IOException {
279     // not a special compaction request, so make our own list
280     List<CompactionRequest> ret = null;
281     if (requests == null) {
282       ret = selectNow ? new ArrayList<CompactionRequest>(r.getStores().size()) : null;
283       for (Store s : r.getStores().values()) {
284         CompactionRequest cr = requestCompactionInternal(r, s, why, p, null, selectNow);
285         if (selectNow) ret.add(cr);
286       }
287     } else {
288       Preconditions.checkArgument(selectNow); // only system requests have selectNow == false
289       ret = new ArrayList<CompactionRequest>(requests.size());
290       for (Pair<CompactionRequest, Store> pair : requests) {
291         ret.add(requestCompaction(r, pair.getSecond(), why, p, pair.getFirst()));
292       }
293     }
294     return ret;
295   }
296 
297   public CompactionRequest requestCompaction(final HRegion r, final Store s,
298       final String why, int priority, CompactionRequest request) throws IOException {
299     return requestCompactionInternal(r, s, why, priority, request, true);
300   }
301 
302   public synchronized void requestSystemCompaction(
303       final HRegion r, final String why) throws IOException {
304     requestCompactionInternal(r, why, Store.NO_PRIORITY, null, false);
305   }
306 
307   public void requestSystemCompaction(
308       final HRegion r, final Store s, final String why) throws IOException {
309     requestCompactionInternal(r, s, why, Store.NO_PRIORITY, null, false);
310   }
311 
312   /**
313    * @param r HRegion store belongs to
314    * @param s Store to request compaction on
315    * @param why Why compaction requested -- used in debug messages
316    * @param priority override the default priority (NO_PRIORITY == decide)
317    * @param request custom compaction request. Can be <tt>null</tt> in which case a simple
318    *          compaction will be used.
319    */
320   private synchronized CompactionRequest requestCompactionInternal(final HRegion r, final Store s,
321       final String why, int priority, CompactionRequest request, boolean selectNow)
322           throws IOException {
323     if (this.server.isStopped()
324         || (r.getTableDesc() != null && !r.getTableDesc().isCompactionEnabled())) {
325       return null;
326     }
327 
328     CompactionContext compaction = null;
329     if (selectNow) {
330       compaction = selectCompaction(r, s, priority, request);
331       if (compaction == null) return null; // message logged inside
332     }
333 
334     // We assume that most compactions are small. So, put system compactions into small
335     // pool; we will do selection there, and move to large pool if necessary.
336     long size = selectNow ? compaction.getRequest().getSize() : 0;
337     ThreadPoolExecutor pool = (!selectNow && s.throttleCompaction(size))
338       ? longCompactions : shortCompactions;
339     pool.execute(new CompactionRunner(s, r, compaction, pool));
340     if (LOG.isDebugEnabled()) {
341       String type = (pool == shortCompactions) ? "Small " : "Large ";
342       LOG.debug(type + "Compaction requested: " + (selectNow ? compaction.toString() : "system")
343           + (why != null && !why.isEmpty() ? "; Because: " + why : "") + "; " + this);
344     }
345     return selectNow ? compaction.getRequest() : null;
346   }
347 
348   private CompactionContext selectCompaction(final HRegion r, final Store s,
349       int priority, CompactionRequest request) throws IOException {
350     CompactionContext compaction = s.requestCompaction(priority, request);
351     if (compaction == null) {
352       if(LOG.isDebugEnabled()) {
353         LOG.debug("Not compacting " + r.getRegionNameAsString() +
354             " because compaction request was cancelled");
355       }
356       return null;
357     }
358     assert compaction.hasSelection();
359     if (priority != Store.NO_PRIORITY) {
360       compaction.getRequest().setPriority(priority);
361     }
362     return compaction;
363   }
364 
365   /**
366    * Only interrupt once it's done with a run through the work loop.
367    */
368   void interruptIfNecessary() {
369     splits.shutdown();
370     mergePool.shutdown();
371     longCompactions.shutdown();
372     shortCompactions.shutdown();
373   }
374 
375   private void waitFor(ThreadPoolExecutor t, String name) {
376     boolean done = false;
377     while (!done) {
378       try {
379         done = t.awaitTermination(60, TimeUnit.SECONDS);
380         LOG.info("Waiting for " + name + " to finish...");
381         if (!done) {
382           t.shutdownNow();
383         }
384       } catch (InterruptedException ie) {
385         LOG.warn("Interrupted waiting for " + name + " to finish...");
386       }
387     }
388   }
389 
390   void join() {
391     waitFor(splits, "Split Thread");
392     waitFor(mergePool, "Merge Thread");
393     waitFor(longCompactions, "Large Compaction Thread");
394     waitFor(shortCompactions, "Small Compaction Thread");
395   }
396 
397   /**
398    * Returns the current size of the queue containing regions that are
399    * processed.
400    *
401    * @return The current size of the regions queue.
402    */
403   public int getCompactionQueueSize() {
404     return longCompactions.getQueue().size() + shortCompactions.getQueue().size();
405   }
406 
407   public int getLargeCompactionQueueSize() {
408     return longCompactions.getQueue().size();
409   }
410 
411 
412   public int getSmallCompactionQueueSize() {
413     return shortCompactions.getQueue().size();
414   }
415 
416   public int getSplitQueueSize() {
417     return splits.getQueue().size();
418   }
419 
420   private boolean shouldSplitRegion() {
421     return (regionSplitLimit > server.getNumberOfOnlineRegions());
422   }
423 
424   /**
425    * @return the regionSplitLimit
426    */
427   public int getRegionSplitLimit() {
428     return this.regionSplitLimit;
429   }
430 
431   @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="EQ_COMPARETO_USE_OBJECT_EQUALS",
432       justification="Contrived use of compareTo")
433   private class CompactionRunner implements Runnable, Comparable<CompactionRunner> {
434     private final Store store;
435     private final HRegion region;
436     private CompactionContext compaction;
437     private int queuedPriority;
438     private ThreadPoolExecutor parent;
439 
440     public CompactionRunner(Store store, HRegion region,
441         CompactionContext compaction, ThreadPoolExecutor parent) {
442       super();
443       this.store = store;
444       this.region = region;
445       this.compaction = compaction;
446       this.queuedPriority = (this.compaction == null)
447           ? store.getCompactPriority() : compaction.getRequest().getPriority();
448       this.parent = parent;
449     }
450 
451     @Override
452     public String toString() {
453       return (this.compaction != null) ? ("Request = " + compaction.getRequest())
454           : ("Store = " + store.toString() + ", pri = " + queuedPriority);
455     }
456 
457     @Override
458     public void run() {
459       Preconditions.checkNotNull(server);
460       if (server.isStopped()
461           || (region.getTableDesc() != null && !region.getTableDesc().isCompactionEnabled())) {
462         return;
463       }
464       // Common case - system compaction without a file selection. Select now.
465       if (this.compaction == null) {
466         int oldPriority = this.queuedPriority;
467         this.queuedPriority = this.store.getCompactPriority();
468         if (this.queuedPriority > oldPriority) {
469           // Store priority decreased while we were in queue (due to some other compaction?),
470           // requeue with new priority to avoid blocking potential higher priorities.
471           this.parent.execute(this);
472           return;
473         }
474         try {
475           this.compaction = selectCompaction(this.region, this.store, queuedPriority, null);
476         } catch (IOException ex) {
477           LOG.error("Compaction selection failed " + this, ex);
478           server.checkFileSystem();
479           return;
480         }
481         if (this.compaction == null) return; // nothing to do
482         // Now see if we are in correct pool for the size; if not, go to the correct one.
483         // We might end up waiting for a while, so cancel the selection.
484         assert this.compaction.hasSelection();
485         ThreadPoolExecutor pool = store.throttleCompaction(
486             compaction.getRequest().getSize()) ? longCompactions : shortCompactions;
487         if (this.parent != pool) {
488           this.store.cancelRequestedCompaction(this.compaction);
489           this.compaction = null;
490           this.parent = pool;
491           this.parent.execute(this);
492           return;
493         }
494       }
495       // Finally we can compact something.
496       assert this.compaction != null;
497 
498       this.compaction.getRequest().beforeExecute();
499       try {
500         // Note: please don't put single-compaction logic here;
501         //       put it into region/store/etc. This is CST logic.
502         long start = EnvironmentEdgeManager.currentTime();
503         boolean completed = region.compact(compaction, store);
504         long now = EnvironmentEdgeManager.currentTime();
505         LOG.info(((completed) ? "Completed" : "Aborted") + " compaction: " +
506               this + "; duration=" + StringUtils.formatTimeDiff(now, start));
507         if (completed) {
508           // degenerate case: blocked regions require recursive enqueues
509           if (store.getCompactPriority() <= 0) {
510             requestSystemCompaction(region, store, "Recursive enqueue");
511           } else {
512             // see if the compaction has caused us to exceed max region size
513             requestSplit(region);
514           }
515         }
516       } catch (IOException ex) {
517         IOException remoteEx =
518             ex instanceof RemoteException ? ((RemoteException) ex).unwrapRemoteException() : ex;
519         LOG.error("Compaction failed " + this, remoteEx);
520         if (remoteEx != ex) {
521           LOG.info("Compaction failed at original callstack: " + formatStackTrace(ex));
522         }
523         server.checkFileSystem();
524       } catch (Exception ex) {
525         LOG.error("Compaction failed " + this, ex);
526         server.checkFileSystem();
527       } finally {
528         LOG.debug("CompactSplitThread Status: " + CompactSplitThread.this);
529       }
530       this.compaction.getRequest().afterExecute();
531     }
532 
533     private String formatStackTrace(Exception ex) {
534       StringWriter sw = new StringWriter();
535       PrintWriter pw = new PrintWriter(sw);
536       ex.printStackTrace(pw);
537       pw.flush();
538       return sw.toString();
539     }
540 
541     @Override
542     public int compareTo(CompactionRunner o) {
543       // Only compare the underlying request (if any), for queue sorting purposes.
544       int compareVal = queuedPriority - o.queuedPriority; // compare priority
545       if (compareVal != 0) return compareVal;
546       CompactionContext tc = this.compaction, oc = o.compaction;
547       // Sort pre-selected (user?) compactions before system ones with equal priority.
548       return (tc == null) ? ((oc == null) ? 0 : 1)
549           : ((oc == null) ? -1 : tc.getRequest().compareTo(oc.getRequest()));
550     }
551   }
552 
553   /**
554    * Cleanup class to use when rejecting a compaction request from the queue.
555    */
556   private static class Rejection implements RejectedExecutionHandler {
557     @Override
558     public void rejectedExecution(Runnable runnable, ThreadPoolExecutor pool) {
559       if (runnable instanceof CompactionRunner) {
560         CompactionRunner runner = (CompactionRunner)runnable;
561         LOG.debug("Compaction Rejected: " + runner);
562         runner.store.cancelRequestedCompaction(runner.compaction);
563       }
564     }
565   }
566 
567   /**
568    * {@inheritDoc}
569    */
570   @Override
571   public void onConfigurationChange(Configuration newConf) {
572     // Check if number of large / small compaction threads has changed, and then
573     // adjust the core pool size of the thread pools, by using the
574     // setCorePoolSize() method. According to the javadocs, it is safe to
575     // change the core pool size on-the-fly. We need to reset the maximum
576     // pool size, as well.
577     int largeThreads = Math.max(1, newConf.getInt(
578             LARGE_COMPACTION_THREADS,
579             LARGE_COMPACTION_THREADS_DEFAULT));
580     if (this.longCompactions.getCorePoolSize() != largeThreads) {
581       LOG.info("Changing the value of " + LARGE_COMPACTION_THREADS +
582               " from " + this.longCompactions.getCorePoolSize() + " to " +
583               largeThreads);
584       this.longCompactions.setMaximumPoolSize(largeThreads);
585       this.longCompactions.setCorePoolSize(largeThreads);
586     }
587 
588     int smallThreads = newConf.getInt(SMALL_COMPACTION_THREADS,
589             SMALL_COMPACTION_THREADS_DEFAULT);
590     if (this.shortCompactions.getCorePoolSize() != smallThreads) {
591       LOG.info("Changing the value of " + SMALL_COMPACTION_THREADS +
592                 " from " + this.shortCompactions.getCorePoolSize() + " to " +
593                 smallThreads);
594       this.shortCompactions.setMaximumPoolSize(smallThreads);
595       this.shortCompactions.setCorePoolSize(smallThreads);
596     }
597 
598     int splitThreads = newConf.getInt(SPLIT_THREADS,
599             SPLIT_THREADS_DEFAULT);
600     if (this.splits.getCorePoolSize() != splitThreads) {
601       LOG.info("Changing the value of " + SPLIT_THREADS +
602                 " from " + this.splits.getCorePoolSize() + " to " +
603                 splitThreads);
604       this.splits.setMaximumPoolSize(smallThreads);
605       this.splits.setCorePoolSize(smallThreads);
606     }
607 
608     int mergeThreads = newConf.getInt(MERGE_THREADS,
609             MERGE_THREADS_DEFAULT);
610     if (this.mergePool.getCorePoolSize() != mergeThreads) {
611       LOG.info("Changing the value of " + MERGE_THREADS +
612                 " from " + this.mergePool.getCorePoolSize() + " to " +
613                 mergeThreads);
614       this.mergePool.setMaximumPoolSize(smallThreads);
615       this.mergePool.setCorePoolSize(smallThreads);
616     }
617 
618     // We change this atomically here instead of reloading the config in order that upstream
619     // would be the only one with the flexibility to reload the config.
620     this.conf.reloadConfiguration();
621   }
622 
623   protected int getSmallCompactionThreadNum() {
624     return this.shortCompactions.getCorePoolSize();
625   }
626 
627   public int getLargeCompactionThreadNum() {
628     return this.longCompactions.getCorePoolSize();
629   }
630 
631   /**
632    * {@inheritDoc}
633    */
634   @Override
635   public void registerChildren(ConfigurationManager manager) {
636     // No children to register.
637   }
638 
639   /**
640    * {@inheritDoc}
641    */
642   @Override
643   public void deregisterChildren(ConfigurationManager manager) {
644     // No children to register
645   }
646 }