mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
14 hours ago 7243c66412f9bc84f84d83d84487ad44e40bd223
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions copyright [year] [name of copyright owner]".
 *
 * Copyright 2026 3A Systems, LLC.
 */
package org.opends.server.tasks;
 
import static org.assertj.core.api.Assertions.assertThat;
import static org.opends.messages.BackendMessages.NOTE_TASK_FINISHED;
import static org.opends.messages.BackendMessages.NOTE_TASK_STARTED;
import static org.opends.messages.ToolMessages.ERR_LDIFEXPORT_NO_BACKENDS_FOR_ID;
import static org.opends.server.config.ConfigConstants.ATTR_TASK_LOG_MESSAGES;
import static org.opends.server.protocols.internal.InternalClientConnection.getRootConnection;
import static org.opends.server.protocols.internal.Requests.newSearchRequest;
 
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
 
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.quicksetup.installer.InstallerHelper;
import org.opends.server.TestCaseUtils;
import org.opends.server.backends.task.TaskState;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.types.Attribute;
import org.opends.server.types.Entry;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
 
/**
 * Tests what the messages a task logs into its entry look like once the task is over, since the
 * quick setup and {@code dsreplication} read them to report and to classify the failure of the
 * tasks they run - see {@code InstallerHelper} and issue #995.
 */
@SuppressWarnings("javadoc")
public class TaskLogMessagesTestCase extends TasksTestCase
{
  @BeforeClass
  public void startServer() throws Exception
  {
    TestCaseUtils.startServer();
  }
 
  /**
   * The failure of a task is reported by neither the first nor the last message it logged: the
   * task scheduler frames the execution with a start and a completion notice, and both of them are
   * in the entry before the terminal task state is, so a reader that waits for that state always
   * sees them. The cause has to be looked up among all the messages.
   */
  @Test
  public void failureOfATaskIsReportedBetweenTheNoticesOfTheScheduler() throws Exception
  {
    final String taskDN = "ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks";
    // Exporting an unknown backend fails the task without touching any data.
    final Entry taskEntry = TestCaseUtils.makeEntry(
        "dn: " + taskDN,
        "objectclass: top",
        "objectclass: ds-task",
        "objectclass: ds-task-export",
        "ds-task-class-name: org.opends.server.tasks.ExportTask",
        "ds-task-export-backend-id: no-such-backend",
        "ds-task-export-ldif-file: " + TestCaseUtils.createTempFile());
    testTask(taskEntry, TaskState.STOPPED_BY_ERROR, 60);
 
    final List<String> logMsgs = getLogMessages(DN.valueOf(taskDN));
    assertThat(logMsgs.size()).isGreaterThanOrEqualTo(3);
    assertThat(logMsgs.get(0)).contains(msgIdField(NOTE_TASK_STARTED.resourceName(), NOTE_TASK_STARTED.ordinal()));
    assertThat(logMsgs.get(logMsgs.size() - 1))
        .contains(msgIdField(NOTE_TASK_FINISHED.resourceName(), NOTE_TASK_FINISHED.ordinal()));
 
    final String failure = InstallerHelper.getRelevantLogMessage(logMsgs);
    assertThat(failure).contains("severity=\"ERROR\"")
        .contains(msgIdField(ERR_LDIFEXPORT_NO_BACKENDS_FOR_ID.resourceName(),
            ERR_LDIFEXPORT_NO_BACKENDS_FOR_ID.ordinal()));
    assertThat(logMsgs.indexOf(failure)).isGreaterThan(0).isLessThan(logMsgs.size() - 1);
    assertThat(new InstallerHelper().isPeersNotFoundError(logMsgs)).isFalse();
  }
 
  /**
   * The id a message is logged with, in the form rendered by
   * {@code org.opends.server.backends.task.Task#addLogMessage}. This is what the classification of
   * a task failure matches on, so the two must agree.
   */
  private String msgIdField(String resourceName, int ordinal)
  {
    return "msgID=" + resourceName + "-" + ordinal;
  }
 
  private List<String> getLogMessages(DN taskDN)
  {
    final InternalSearchOperation searchOperation =
        getRootConnection().processSearch(newSearchRequest(taskDN, SearchScope.BASE_OBJECT));
    final Entry taskEntry = searchOperation.getSearchEntries().getFirst();
 
    final List<String> logMsgs = new ArrayList<>();
    for (Attribute attribute : taskEntry.getAllAttributes(ATTR_TASK_LOG_MESSAGES))
    {
      for (ByteString value : attribute)
      {
        logMsgs.add(value.toString());
      }
    }
    return logMsgs;
  }
}