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