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 2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2012-2014 ForgeRock AS
026 */
027package org.opends.server.tools.dsreplication;
028
029import static org.opends.messages.AdminToolMessages.*;
030import static org.opends.messages.CoreMessages.*;
031
032import java.io.File;
033
034import org.forgerock.i18n.LocalizableMessage;
035import org.forgerock.i18n.slf4j.LocalizedLogger;
036
037import org.opends.quicksetup.util.ProgressMessageFormatter;
038import org.opends.server.replication.plugin.LDAPReplicationDomain;
039import org.opends.server.types.DN;
040import org.opends.server.types.DirectoryEnvironmentConfig;
041import org.opends.server.types.DirectoryException;
042import org.opends.server.types.OpenDsException;
043import org.forgerock.opendj.ldap.ResultCode;
044import org.opends.server.util.EmbeddedUtils;
045import org.opends.server.util.StaticUtils;
046import org.opends.server.util.TimeThread;
047import com.forgerock.opendj.cli.ConsoleApplication;
048import org.opends.server.util.cli.PointAdder;
049
050/**
051 * The class that is in charge of taking the different information provided
052 * by the user through the command-line and actually executing the local
053 * purge.
054 *
055 */
056public class LocalPurgeHistorical
057{
058  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
059
060  private final PurgeHistoricalUserData uData;
061  private final ConsoleApplication app;
062  private final ProgressMessageFormatter formatter;
063  private final String configFile;
064  private final String configClass;
065
066  /**
067   * The default constructor.
068   * @param uData the object containing the information provided by the user.
069   * @param app the console application that is used to write the progress
070   * and error messages.
071   * @param formatter the formatter to be used to generated the messages.
072   * @param configFile the file that contains the configuration.  This is
073   * required to initialize properly the server.
074   * @param configClass the class to be used to read the configuration.  This is
075   * required to initialize properly the server.
076   */
077  public LocalPurgeHistorical(PurgeHistoricalUserData uData,
078      ConsoleApplication app,
079      ProgressMessageFormatter formatter, String configFile,
080      String configClass)
081  {
082    this.uData = uData;
083    this.app = app;
084    this.formatter = formatter;
085    this.configFile = configFile;
086    this.configClass = configClass;
087  }
088
089  /**
090   * Executes the purge historical operation locally.
091   * @return the result code.
092   */
093  public ReplicationCliReturnCode execute()
094  {
095    boolean applyTimeout = uData.getMaximumDuration() > 0;
096
097    long startTime = TimeThread.getTime();
098    long purgeMaxTime = getTimeoutInSeconds() * 1000L;
099
100    long endMaxTime = startTime + purgeMaxTime;
101
102    app.print(formatter.getFormattedProgress(
103        INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_ENVIRONMENT.get()));
104
105    PointAdder pointAdder = new PointAdder(app);
106    pointAdder.start();
107
108    Class<?> cfgClass;
109
110    try
111    {
112      cfgClass = Class.forName(configClass);
113    }
114    catch (Exception e)
115    {
116      pointAdder.stop();
117      LocalizableMessage message =
118        ERR_CANNOT_LOAD_CONFIG_HANDLER_CLASS.get(
119            configClass, StaticUtils.stackTraceToSingleLineString(e));
120      app.println(message);
121      logger.error(LocalizableMessage.raw("Error loading configuration class "+configClass+
122          ": "+e, e));
123      return ReplicationCliReturnCode.ERROR_LOCAL_PURGE_HISTORICAL_CLASS_LOAD;
124    }
125
126    try
127    {
128      // Create a configuration for the server.
129      DirectoryEnvironmentConfig environmentConfig =
130        new DirectoryEnvironmentConfig();
131      environmentConfig.setConfigClass(cfgClass);
132      environmentConfig.setConfigFile(new File(configFile));
133      environmentConfig.setDisableConnectionHandlers(true);
134      EmbeddedUtils.startServer(environmentConfig);
135    }
136    catch (OpenDsException ode)
137    {
138      pointAdder.stop();
139      LocalizableMessage message = ode.getMessageObject();
140        ERR_CANNOT_LOAD_CONFIG_HANDLER_CLASS.get(
141            configClass, StaticUtils.stackTraceToSingleLineString(ode));
142      app.println(message);
143      logger.error(LocalizableMessage.raw("Error starting server with file "+configFile+
144          ": "+ode, ode));
145      return ReplicationCliReturnCode.ERROR_LOCAL_PURGE_HISTORICAL_SERVER_START;
146    }
147    pointAdder.stop();
148    app.print(formatter.getFormattedDone());
149    app.println();
150    app.println();
151    app.print(formatter.getFormattedProgress(
152        INFO_REPLICATION_PURGE_HISTORICAL_LOCAL_STARTING.get()));
153    app.println();
154
155    if (applyTimeout && timeoutOccurred(endMaxTime))
156    {
157      return handleTimeout();
158    }
159
160    try
161    {
162      // launch the job
163      for (String baseDN : uData.getBaseDNs())
164      {
165        DN dn = DN.valueOf(baseDN);
166        // We can assume that this is an LDAP replication domain
167        LDAPReplicationDomain domain =
168            LDAPReplicationDomain.retrievesReplicationDomain(dn);
169
170        domain.purgeConflictsHistorical(null, startTime + purgeMaxTime);
171      }
172
173    }
174    catch (DirectoryException de)
175    {
176      if (de.getResultCode() == ResultCode.ADMIN_LIMIT_EXCEEDED)
177      {
178        return handleTimeout();
179      }
180      else
181      {
182        return handleGenericExecuting(de);
183      }
184    }
185    return ReplicationCliReturnCode.SUCCESSFUL;
186  }
187
188  private ReplicationCliReturnCode handleGenericExecuting(OpenDsException ode)
189  {
190    logger.error(LocalizableMessage.raw("Error executing purge historical: "+ode, ode));
191    app.println();
192    app.println(ERR_REPLICATION_PURGE_HISTORICAL_EXECUTING.get(
193        ode.getMessageObject()));
194    return ReplicationCliReturnCode.ERROR_LOCAL_PURGE_HISTORICAL_EXECUTING;
195  }
196
197  private ReplicationCliReturnCode handleTimeout()
198  {
199    app.println();
200    app.println(ERR_REPLICATION_PURGE_HISTORICAL_TIMEOUT.get(
201        getTimeoutInSeconds()));
202    return ReplicationCliReturnCode.ERROR_LOCAL_PURGE_HISTORICAL_TIMEOUT;
203  }
204
205  /**
206   * Returns the time-out provided by the user in seconds.
207   * @return the time-out provided by the user in seconds.
208   */
209  private int getTimeoutInSeconds()
210  {
211    return uData.getMaximumDuration();
212  }
213
214  /**
215   * A method that tells whether the maximum time to execute the operation was
216   * elapsed or not.
217   * @param endMaxTime the latest time in milliseconds when the operation should
218   * be completed.
219   * @return {@code true} if the time-out occurred and {@code false} otherwise.
220   */
221  private boolean timeoutOccurred(long endMaxTime)
222  {
223    return TimeThread.getTime() > endMaxTime;
224  }
225}