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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS
026 */
027package org.opends.server.loggers;
028
029import static org.opends.messages.LoggerMessages.*;
030import static org.opends.server.util.StaticUtils.*;
031
032import java.io.File;
033import java.util.ArrayList;
034import java.util.Arrays;
035import java.util.List;
036
037import org.forgerock.i18n.LocalizableMessage;
038import org.forgerock.i18n.slf4j.LocalizedLogger;
039import org.opends.server.admin.server.ConfigurationChangeListener;
040import org.opends.server.admin.std.server.FreeDiskSpaceLogRetentionPolicyCfg;
041import org.opends.server.core.DirectoryServer;
042import org.forgerock.opendj.config.server.ConfigChangeResult;
043import org.opends.server.types.DirectoryException;
044
045/**
046 * This class implements a retention policy based on the free disk space
047 * available expressed as a percentage.
048 */
049public class FreeDiskSpaceRetentionPolicy implements
050    RetentionPolicy<FreeDiskSpaceLogRetentionPolicyCfg>,
051    ConfigurationChangeListener<FreeDiskSpaceLogRetentionPolicyCfg>
052{
053  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
054
055  private long freeDiskSpace;
056  private FreeDiskSpaceLogRetentionPolicyCfg config;
057
058  /** {@inheritDoc} */
059  @Override
060  public void initializeLogRetentionPolicy(
061      FreeDiskSpaceLogRetentionPolicyCfg config)
062  {
063    this.freeDiskSpace = config.getFreeDiskSpace();
064    this.config = config;
065
066    config.addFreeDiskSpaceChangeListener(this);
067  }
068
069  /** {@inheritDoc} */
070  @Override
071  public boolean isConfigurationChangeAcceptable(
072      FreeDiskSpaceLogRetentionPolicyCfg config,
073      List<LocalizableMessage> unacceptableReasons)
074  {
075    // Changes should always be OK
076    return true;
077  }
078
079  /** {@inheritDoc} */
080  @Override
081  public ConfigChangeResult applyConfigurationChange(
082      FreeDiskSpaceLogRetentionPolicyCfg config)
083  {
084    this.freeDiskSpace = config.getFreeDiskSpace();
085    this.config = config;
086
087    return new ConfigChangeResult();
088  }
089
090  /** {@inheritDoc} */
091  @Override
092  public File[] deleteFiles(FileNamingPolicy fileNamingPolicy)
093      throws DirectoryException
094  {
095    File[] files = fileNamingPolicy.listFiles();
096    if(files == null)
097    {
098      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
099          ERR_LOGGER_ERROR_LISTING_FILES.get(fileNamingPolicy.getInitialName()));
100    }
101
102    if(files.length <= 0)
103    {
104      return new File[0];
105    }
106
107    long freeSpace = 0;
108    try
109    {
110      freeSpace = files[0].getFreeSpace();
111    }
112    catch (Exception e)
113    {
114      logger.traceException(e);
115      LocalizableMessage message =
116          ERR_LOGGER_ERROR_OBTAINING_FREE_SPACE.get(files[0],
117              stackTraceToSingleLineString(e));
118      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
119                                   message, e);
120    }
121
122    logger.trace("Current free disk space: %d, Required: %d", freeSpace,
123          freeDiskSpace);
124
125    if (freeSpace > freeDiskSpace)
126    {
127      // No cleaning needed.
128      return new File[0];
129    }
130
131    long freeSpaceNeeded = freeDiskSpace - freeSpace;
132
133    // Sort files based on last modified time.
134    Arrays.sort(files, new FileComparator());
135
136    List<File> filesToDelete = new ArrayList<>();
137    long freedSpace = 0;
138    for (int j = files.length - 1; j > 1; j--)
139    {
140      freedSpace += files[j].length();
141      filesToDelete.add(files[j]);
142      if (freedSpace >= freeSpaceNeeded)
143      {
144        break;
145      }
146    }
147    return filesToDelete.toArray(new File[filesToDelete.size()]);
148  }
149
150  /** {@inheritDoc} */
151  @Override
152  public String toString()
153  {
154    return "Free Disk Retention Policy " + config.dn();
155  }
156}
157