Skip to content

Commit b260927

Browse files
Healthcheck process (#853)
* Checkouted files from the `healthcheck-process` branch and updated the code, also added integration test. * Changed the name of the test. * Updated the description to be consistent with another descriptions. * Refactored the calling the check.run method, use `check.report` instead.
1 parent ca40fa9 commit b260927

8 files changed

Lines changed: 344 additions & 4 deletions

File tree

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
package org.dspace.app.healthreport;
9+
10+
import static org.apache.commons.io.IOUtils.toInputStream;
11+
12+
import java.io.IOException;
13+
import java.io.InputStream;
14+
import java.nio.charset.StandardCharsets;
15+
import java.text.SimpleDateFormat;
16+
import java.util.Date;
17+
import java.util.LinkedHashMap;
18+
import java.util.Locale;
19+
import java.util.Map;
20+
import javax.mail.MessagingException;
21+
22+
import org.apache.commons.cli.ParseException;
23+
import org.apache.logging.log4j.LogManager;
24+
import org.apache.logging.log4j.Logger;
25+
import org.dspace.core.Context;
26+
import org.dspace.core.Email;
27+
import org.dspace.core.I18nUtil;
28+
import org.dspace.eperson.factory.EPersonServiceFactory;
29+
import org.dspace.eperson.service.EPersonService;
30+
import org.dspace.health.Check;
31+
import org.dspace.health.Report;
32+
import org.dspace.health.ReportInfo;
33+
import org.dspace.scripts.DSpaceRunnable;
34+
import org.dspace.services.ConfigurationService;
35+
import org.dspace.services.factory.DSpaceServicesFactory;
36+
import org.dspace.utils.DSpace;
37+
38+
/**
39+
* This class is used to generate a health report of the DSpace instance.
40+
* @author Matus Kasak (dspace at dataquest.sk)
41+
* @author Milan Majchrak (dspace at dataquest.sk)
42+
*/
43+
public class HealthReport extends DSpaceRunnable<HealthReportScriptConfiguration> {
44+
ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
45+
private static final Logger log = LogManager.getLogger(HealthReport.class);
46+
private EPersonService ePersonService;
47+
48+
/**
49+
* Checks to be performed.
50+
*/
51+
private static final LinkedHashMap<String, Check> checks = Report.checks();
52+
53+
/**
54+
* `-i`: Info, show help information.
55+
*/
56+
private boolean info = false;
57+
58+
/**
59+
* `-e`: Email, send report to specified email address.
60+
*/
61+
private String email;
62+
63+
/**
64+
* `-c`: Check, perform only specific check by index (0-`getNumberOfChecks()`).
65+
*/
66+
private int specificCheck = -1;
67+
68+
/**
69+
* `-f`: For, specify the last N days to consider.
70+
* Default value is set in dspace.cfg.
71+
*/
72+
private int forLastNDays = configurationService.getIntProperty("healthcheck.last_n_days");
73+
74+
/**
75+
* `-o`: Output, specify a file to save the report.
76+
*/
77+
private String fileName;
78+
79+
@Override
80+
public HealthReportScriptConfiguration getScriptConfiguration() {
81+
return new DSpace().getServiceManager()
82+
.getServiceByName("health-report", HealthReportScriptConfiguration.class);
83+
}
84+
85+
@Override
86+
public void setup() throws ParseException {
87+
ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
88+
// `-i`: Info, show help information.
89+
if (commandLine.hasOption('i')) {
90+
info = true;
91+
return;
92+
}
93+
94+
// `-e`: Email, send report to specified email address.
95+
if (commandLine.hasOption('e')) {
96+
email = commandLine.getOptionValue('e');
97+
handler.logInfo("\nReport sent to this email address: " + email);
98+
}
99+
100+
// `-c`: Check, perform only specific check by index (0-`getNumberOfChecks()`).
101+
if (commandLine.hasOption('c')) {
102+
String checkOption = commandLine.getOptionValue('c');
103+
try {
104+
specificCheck = Integer.parseInt(checkOption);
105+
if (specificCheck < 0 || specificCheck >= getNumberOfChecks()) {
106+
specificCheck = -1;
107+
}
108+
} catch (NumberFormatException e) {
109+
log.info("Invalid value for check. It has to be a number from the displayed range.");
110+
return;
111+
}
112+
}
113+
114+
// `-f`: For, specify the last N days to consider.
115+
if (commandLine.hasOption('f')) {
116+
String daysOption = commandLine.getOptionValue('f');
117+
try {
118+
forLastNDays = Integer.parseInt(daysOption);
119+
} catch (NumberFormatException e) {
120+
log.info("Invalid value for last N days. Argument f has to be a number.");
121+
return;
122+
}
123+
}
124+
125+
// `-o`: Output, specify a file to save the report.
126+
if (commandLine.hasOption('o')) {
127+
fileName = commandLine.getOptionValue('o');
128+
}
129+
}
130+
131+
@Override
132+
public void internalRun() throws Exception {
133+
if (info) {
134+
printHelp();
135+
return;
136+
}
137+
138+
ReportInfo ri = new ReportInfo(this.forLastNDays);
139+
140+
StringBuilder sbReport = new StringBuilder();
141+
sbReport.append("\n\nHEALTH REPORT:\n");
142+
143+
int position = -1;
144+
for (Map.Entry<String, Check> check_entry : Report.checks().entrySet()) {
145+
++position;
146+
if (specificCheck != -1 && specificCheck != position) {
147+
continue;
148+
}
149+
150+
String name = check_entry.getKey();
151+
Check check = check_entry.getValue();
152+
153+
log.info("#{}. Processing [{}] at [{}]", position, name, new SimpleDateFormat(
154+
"yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));
155+
156+
sbReport.append("\n######################\n\n").append(name).append(":\n");
157+
check.report(ri);
158+
sbReport.append(check.getReport());
159+
}
160+
161+
// save output to file
162+
if (fileName != null) {
163+
Context context = new Context();
164+
context.setCurrentUser(ePersonService.find(context, this.getEpersonIdentifier()));
165+
166+
InputStream inputStream = toInputStream(sbReport.toString(), StandardCharsets.UTF_8);
167+
handler.writeFilestream(context, fileName, inputStream, "export");
168+
169+
context.restoreAuthSystemState();
170+
context.complete();
171+
}
172+
173+
// send email to email address from argument
174+
if (email != null) {
175+
try {
176+
Email e = Email.getEmail(I18nUtil.getEmailFilename(Locale.getDefault(), "healthcheck"));
177+
e.addRecipient(email);
178+
e.addArgument(sbReport.toString());
179+
e.send();
180+
} catch (IOException | MessagingException e) {
181+
log.error("Error sending email:", e);
182+
}
183+
}
184+
185+
handler.logInfo(sbReport.toString());
186+
}
187+
188+
@Override
189+
public void printHelp() {
190+
handler.logInfo("\n\nINFORMATION\nThis process creates a health report of your DSpace.\n" +
191+
"You can choose from these available options:\n" +
192+
" -i, --info Show help information\n" +
193+
" -e, --email Send report to specified email address\n" +
194+
" -c, --check Perform only specific check by index (0-" + (getNumberOfChecks() - 1) + ")\n" +
195+
" -f, --for Specify the last N days to consider\n" +
196+
" -o, --output Specify a file to save the report\n\n" +
197+
"If you want to execute only one check using -c, use check index:\n" + checksNamesToString() + "\n"
198+
);
199+
}
200+
201+
/**
202+
* Convert checks names to string.
203+
*/
204+
private String checksNamesToString() {
205+
StringBuilder names = new StringBuilder();
206+
int pos = 0;
207+
for (String name : checks.keySet()) {
208+
names.append(String.format(" %d. %s\n", pos++, name));
209+
}
210+
return names.toString();
211+
}
212+
213+
/**
214+
* Get the number of checks. This is used for the `-c` option.
215+
*/
216+
public static int getNumberOfChecks() {
217+
return checks.size();
218+
}
219+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
package org.dspace.app.healthreport;
9+
10+
import org.apache.commons.cli.Options;
11+
import org.dspace.scripts.configuration.ScriptConfiguration;
12+
13+
/**
14+
* This class represents a HealthReport that is used in the CLI.
15+
* @author Matus Kasak (dspace at dataquest.sk)
16+
*/
17+
public class HealthReportScriptConfiguration<T extends HealthReport> extends ScriptConfiguration<T> {
18+
19+
private Class<T> dspaceRunnableclass;
20+
21+
@Override
22+
public Class<T> getDspaceRunnableClass() {
23+
return dspaceRunnableclass;
24+
}
25+
26+
@Override
27+
public void setDspaceRunnableClass(Class<T> dspaceRunnableClass) {
28+
this.dspaceRunnableclass = dspaceRunnableClass;
29+
}
30+
31+
@Override
32+
public Options getOptions() {
33+
if (options == null) {
34+
Options options = new Options();
35+
options.addOption("i", "info", false,
36+
"Show help information.");
37+
options.addOption("e", "email", true,
38+
"Send report to this email address.");
39+
options.getOption("e").setType(String.class);
40+
options.addOption("c", "check", true,
41+
String.format("Perform only specific check (use index from 0 to %d, " +
42+
"otherwise perform default checks).", HealthReport.getNumberOfChecks() - 1));
43+
options.getOption("c").setType(String.class);
44+
options.addOption("f", "for", true,
45+
"Report for last N days. Used only in general information for now.");
46+
options.getOption("f").setType(String.class);
47+
options.addOption("o", "output", true,
48+
"Save report to the file.");
49+
50+
super.options = options;
51+
}
52+
return options;
53+
}
54+
}

dspace-api/src/main/java/org/dspace/health/Check.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,15 @@ protected void error(Throwable e, String msg) {
5050
}
5151
}
5252

53+
public String getErrors() {
54+
return errors_;
55+
}
56+
57+
public String getReport() {
58+
return report_;
59+
}
60+
61+
public long getTook() {
62+
return took_;
63+
}
5364
}

dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,9 @@
9696
<property name="dspaceRunnableClass" value="org.dspace.administer.FileDownloader"/>
9797
</bean>
9898

99+
<bean id="health-report" class="org.dspace.app.healthreport.HealthReportScriptConfiguration" primary="true">
100+
<property name="description" value="Get report about DSpace health"/>
101+
<property name="dspaceRunnableClass" value="org.dspace.app.healthreport.HealthReport"/>
102+
</bean>
103+
99104
</beans>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
package org.dspace.scripts;
9+
10+
import static org.hamcrest.CoreMatchers.containsString;
11+
import static org.hamcrest.MatcherAssert.assertThat;
12+
import static org.hamcrest.Matchers.empty;
13+
import static org.hamcrest.Matchers.hasItem;
14+
import static org.hamcrest.Matchers.hasSize;
15+
16+
import java.util.List;
17+
18+
import org.dspace.AbstractIntegrationTestWithDatabase;
19+
import org.dspace.app.launcher.ScriptLauncher;
20+
import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler;
21+
import org.junit.Test;
22+
23+
/**
24+
* Integration test for the HealthReport script
25+
* @author Milan Majchrak (milan.majchrak at dataquest.sk)
26+
*/
27+
public class HealthReportIT extends AbstractIntegrationTestWithDatabase {
28+
@Test
29+
public void testDefaultHealthcheckRun() throws Exception {
30+
31+
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
32+
33+
String[] args = new String[] { "health-report" };
34+
ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testDSpaceRunnableHandler, kernelImpl);
35+
36+
assertThat(testDSpaceRunnableHandler.getErrorMessages(), empty());
37+
assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty());
38+
39+
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
40+
assertThat(messages, hasSize(1));
41+
assertThat(messages, hasItem(containsString("HEALTH REPORT:")));
42+
}
43+
}

dspace/config/modules/healthcheck.cfg

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
# If you use the Pre-DSpace-3.0 embargo feature, you might want to
66
# add 'Embargo items (Pre-3.0),' to the following list.
77
healthcheck.checks = General Information,\
8-
Checksum,\
98
Item summary,\
10-
User summary,\
11-
Log Analyser Check
9+
User summary
1210

1311
plugin.named.org.dspace.health.Check = \
1412
org.dspace.health.InfoCheck = General Information,\
@@ -18,5 +16,5 @@ plugin.named.org.dspace.health.Check = \
1816
org.dspace.health.UserCheck = User summary,\
1917
org.dspace.health.LogAnalyserCheck = Log Analyser Check
2018

21-
# report from the last N days (where dates are applicable)
19+
# default value of the report from the last N days (where dates are applicable)
2220
healthcheck.last_n_days = 7

dspace/config/spring/api/scripts.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,9 @@
9191
<property name="dspaceRunnableClass" value="org.dspace.app.bulkaccesscontrol.BulkAccessControlCli"/>
9292
</bean>
9393

94+
<bean id="health-report" class="org.dspace.app.healthreport.HealthReportScriptConfiguration" primary="true">
95+
<property name="description" value="Get report about DSpace health"/>
96+
<property name="dspaceRunnableClass" value="org.dspace.app.healthreport.HealthReport"/>
97+
</bean>
98+
9499
</beans>

dspace/config/spring/rest/scripts.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,9 @@
7474
<property name="dspaceRunnableClass" value="org.dspace.administer.FileDownloader"/>
7575
</bean>
7676

77+
<bean id="health-report" class="org.dspace.app.healthreport.HealthReportScriptConfiguration" primary="true">
78+
<property name="description" value="Get report about DSpace health"/>
79+
<property name="dspaceRunnableClass" value="org.dspace.app.healthreport.HealthReport"/>
80+
</bean>
81+
7782
</beans>

0 commit comments

Comments
 (0)