diff --git a/src/main/java/org/codelibs/fess/Constants.java b/src/main/java/org/codelibs/fess/Constants.java index c703de390..9331625fa 100644 --- a/src/main/java/org/codelibs/fess/Constants.java +++ b/src/main/java/org/codelibs/fess/Constants.java @@ -136,6 +136,8 @@ public class Constants extends CoreLibConstants { public static final String NOTIFICATION_TO_PROPERTY = "notification.to"; + public static final String SLACK_WEBHOOK_URLS_PROPERTY = "slack.webhook.urls"; + public static final String USE_BROWSER_LOCALE_FOR_SEARCH_PROPERTY = "search.use.browser.locale"; public static final String SUGGEST_SEARCH_LOG_PROPERTY = "suggest.searchlog"; diff --git a/src/main/java/org/codelibs/fess/exec/Crawler.java b/src/main/java/org/codelibs/fess/exec/Crawler.java index a05e304fe..6736b3db1 100644 --- a/src/main/java/org/codelibs/fess/exec/Crawler.java +++ b/src/main/java/org/codelibs/fess/exec/Crawler.java @@ -53,6 +53,7 @@ import org.codelibs.fess.exception.ContainerNotAvailableException; import org.codelibs.fess.helper.CrawlingInfoHelper; import org.codelibs.fess.helper.DataIndexHelper; import org.codelibs.fess.helper.DuplicateHostHelper; +import org.codelibs.fess.helper.NotificationHelper; import org.codelibs.fess.helper.PathMappingHelper; import org.codelibs.fess.helper.WebFsIndexHelper; import org.codelibs.fess.mylasta.direction.FessConfig; @@ -60,6 +61,7 @@ import org.codelibs.fess.mylasta.mail.CrawlerPostcard; import org.codelibs.fess.timer.SystemMonitorTarget; import org.codelibs.fess.util.ComponentUtil; import org.codelibs.fess.util.ThreadDumpUtil; +import org.dbflute.mail.send.hook.SMailCallbackContext; import org.elasticsearch.monitor.jvm.JvmInfo; import org.elasticsearch.monitor.os.OsProbe; import org.elasticsearch.monitor.process.ProcessProbe; @@ -378,9 +380,7 @@ public class Crawler { protected void sendMail(final Map infoMap) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); - final String toStrs = fessConfig.getNotificationTo(); - if (StringUtil.isNotBlank(toStrs)) { - final String[] toAddresses = toStrs.split(","); + if (fessConfig.hasNotification()) { final Map dataMap = new HashMap<>(); for (final Map.Entry entry : infoMap.entrySet()) { dataMap.put(StringUtil.decapitalize(entry.getKey()), entry.getValue()); @@ -395,34 +395,52 @@ public class Crawler { logger.debug("\ninfoMap: {}\ndataMap: {}", infoMap, dataMap); final DynamicProperties systemProperties = ComponentUtil.getSystemProperties(); + final String toStrs = fessConfig.getNotificationTo(); final Postbox postbox = ComponentUtil.getComponent(Postbox.class); - CrawlerPostcard.droppedInto(postbox, postcard -> { - postcard.setFrom(fessConfig.getMailFromAddress(), fessConfig.getMailFromName()); - postcard.addReplyTo(fessConfig.getMailReturnPath()); - stream(toAddresses).of(stream -> stream.forEach(address -> { - postcard.addTo(address); - })); - postcard.setCrawlerEndTime(getValueFromMap(dataMap, "crawlerEndTime", StringUtil.EMPTY)); - postcard.setCrawlerExecTime(getValueFromMap(dataMap, "crawlerExecTime", "0")); - postcard.setCrawlerStartTime(getValueFromMap(dataMap, "crawlerStartTime", StringUtil.EMPTY)); - postcard.setDataCrawlEndTime(getValueFromMap(dataMap, "dataCrawlEndTime", StringUtil.EMPTY)); - postcard.setDataCrawlExecTime(getValueFromMap(dataMap, "dataCrawlExecTime", "0")); - postcard.setDataCrawlStartTime(getValueFromMap(dataMap, "dataCrawlStartTime", StringUtil.EMPTY)); - postcard.setDataIndexSize(getValueFromMap(dataMap, "dataIndexSize", "0")); - postcard.setDataIndexExecTime(getValueFromMap(dataMap, "dataIndexExecTime", "0")); - postcard.setHostname(getValueFromMap(dataMap, "hostname", StringUtil.EMPTY)); - postcard.setWebFsCrawlEndTime(getValueFromMap(dataMap, "webFsCrawlEndTime", StringUtil.EMPTY)); - postcard.setWebFsCrawlExecTime(getValueFromMap(dataMap, "webFsCrawlExecTime", "0")); - postcard.setWebFsCrawlStartTime(getValueFromMap(dataMap, "webFsCrawlStartTime", StringUtil.EMPTY)); - postcard.setWebFsIndexExecTime(getValueFromMap(dataMap, "webFsIndexExecTime", "0")); - postcard.setWebFsIndexSize(getValueFromMap(dataMap, "webFsIndexSize", "0")); - if (Constants.TRUE.equalsIgnoreCase(infoMap.get(Constants.CRAWLER_STATUS))) { - postcard.setStatus(Constants.OK); + try { + final String[] toAddresses; + if (StringUtil.isNotBlank(toStrs)) { + toAddresses = toStrs.split(","); } else { - postcard.setStatus(Constants.FAIL); + toAddresses = StringUtil.EMPTY_STRINGS; } - postcard.setJobname(systemProperties.getProperty("job.runtime.name", StringUtil.EMPTY)); - }); + final NotificationHelper notificationHelper = ComponentUtil.getNotificationHelper(); + SMailCallbackContext.setPreparedMessageHookOnThread(notificationHelper::send); + CrawlerPostcard.droppedInto(postbox, postcard -> { + postcard.setFrom(fessConfig.getMailFromAddress(), fessConfig.getMailFromName()); + postcard.addReplyTo(fessConfig.getMailReturnPath()); + if (toAddresses.length > 0) { + stream(toAddresses).of(stream -> stream.map(String::trim).forEach(address -> { + postcard.addTo(address); + })); + } else { + postcard.addTo(fessConfig.getMailFromAddress()); + postcard.dryrun(); + } + postcard.setCrawlerEndTime(getValueFromMap(dataMap, "crawlerEndTime", StringUtil.EMPTY)); + postcard.setCrawlerExecTime(getValueFromMap(dataMap, "crawlerExecTime", "0")); + postcard.setCrawlerStartTime(getValueFromMap(dataMap, "crawlerStartTime", StringUtil.EMPTY)); + postcard.setDataCrawlEndTime(getValueFromMap(dataMap, "dataCrawlEndTime", StringUtil.EMPTY)); + postcard.setDataCrawlExecTime(getValueFromMap(dataMap, "dataCrawlExecTime", "0")); + postcard.setDataCrawlStartTime(getValueFromMap(dataMap, "dataCrawlStartTime", StringUtil.EMPTY)); + postcard.setDataIndexSize(getValueFromMap(dataMap, "dataIndexSize", "0")); + postcard.setDataIndexExecTime(getValueFromMap(dataMap, "dataIndexExecTime", "0")); + postcard.setHostname(getValueFromMap(dataMap, "hostname", StringUtil.EMPTY)); + postcard.setWebFsCrawlEndTime(getValueFromMap(dataMap, "webFsCrawlEndTime", StringUtil.EMPTY)); + postcard.setWebFsCrawlExecTime(getValueFromMap(dataMap, "webFsCrawlExecTime", "0")); + postcard.setWebFsCrawlStartTime(getValueFromMap(dataMap, "webFsCrawlStartTime", StringUtil.EMPTY)); + postcard.setWebFsIndexExecTime(getValueFromMap(dataMap, "webFsIndexExecTime", "0")); + postcard.setWebFsIndexSize(getValueFromMap(dataMap, "webFsIndexSize", "0")); + if (Constants.TRUE.equalsIgnoreCase(infoMap.get(Constants.CRAWLER_STATUS))) { + postcard.setStatus(Constants.OK); + } else { + postcard.setStatus(Constants.FAIL); + } + postcard.setJobname(systemProperties.getProperty("job.runtime.name", StringUtil.EMPTY)); + }); + } finally { + SMailCallbackContext.clearPreparedMessageHookOnThread(); + } } } diff --git a/src/main/java/org/codelibs/fess/helper/NotificationHelper.java b/src/main/java/org/codelibs/fess/helper/NotificationHelper.java new file mode 100644 index 000000000..710d82f25 --- /dev/null +++ b/src/main/java/org/codelibs/fess/helper/NotificationHelper.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012-2020 CodeLibs Project and the Others. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package org.codelibs.fess.helper; + +import java.io.IOException; + +import org.apache.commons.text.StringEscapeUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.codelibs.core.lang.StringUtil; +import org.codelibs.core.stream.StreamUtil; +import org.codelibs.curl.Curl; +import org.codelibs.curl.CurlResponse; +import org.codelibs.fess.mylasta.direction.FessConfig; +import org.codelibs.fess.util.ComponentUtil; +import org.dbflute.mail.CardView; +import org.dbflute.mail.send.supplement.SMailPostingDiscloser; + +public class NotificationHelper { + private static final Logger logger = LogManager.getLogger(NotificationHelper.class); + + protected static final String LF = "\n"; + + public void send(final CardView cardView, final SMailPostingDiscloser discloser) { + sendToSlack(cardView, discloser); + } + + protected void sendToSlack(CardView cardView, SMailPostingDiscloser discloser) { + // https://api.slack.com/messaging/webhooks#posting_with_webhooks + final FessConfig fessConfig = ComponentUtil.getFessConfig(); + final String slackWebhookUrls = fessConfig.getSlackWebhookUrls(); + if (StringUtil.isBlank(slackWebhookUrls)) { + return; + } + final StringBuilder buf = new StringBuilder(); + final String body = + buf.append("{\"text\":\"").append(StringEscapeUtils.escapeJson(toSlackMessage(discloser))).append("\"}").toString(); + StreamUtil.split(slackWebhookUrls, "[,\\s]").of( + stream -> stream.filter(StringUtil::isNotBlank).forEach( + url -> { + try (CurlResponse response = Curl.post(url).header("Content-Type", "application/json").body(body).execute()) { + if (response.getHttpStatusCode() == 200) { + if (logger.isDebugEnabled()) { + logger.debug("Sent {} to {}.", body, url); + } + } else { + logger.warn("Failed to send {} to {}. HTTP Status is {}. {}", body, url, response.getHttpStatusCode(), + response.getContentAsString()); + } + } catch (final IOException e) { + logger.warn("Failed to send {} to {}.", body, url, e); + } + })); + } + + protected String toSlackMessage(final SMailPostingDiscloser discloser) { + final StringBuilder sb = new StringBuilder(); + sb.append(LF).append(discloser.getSavedSubject().orElse(StringUtil.EMPTY).trim()); + sb.append(LF).append("```"); + sb.append(LF).append(discloser.getSavedPlainText().orElse(StringUtil.EMPTY).trim()); + sb.append(LF).append("```"); + return sb.toString(); + + } +} diff --git a/src/main/java/org/codelibs/fess/job/PingEsJob.java b/src/main/java/org/codelibs/fess/job/PingEsJob.java index 59095a7b8..8ed60f11b 100644 --- a/src/main/java/org/codelibs/fess/job/PingEsJob.java +++ b/src/main/java/org/codelibs/fess/job/PingEsJob.java @@ -15,15 +15,19 @@ */ package org.codelibs.fess.job; +import static org.codelibs.core.stream.StreamUtil.stream; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.codelibs.core.lang.StringUtil; import org.codelibs.fess.entity.PingResponse; import org.codelibs.fess.es.client.FessEsClient; +import org.codelibs.fess.helper.NotificationHelper; import org.codelibs.fess.helper.SystemHelper; import org.codelibs.fess.mylasta.direction.FessConfig; import org.codelibs.fess.mylasta.mail.EsStatusPostcard; import org.codelibs.fess.util.ComponentUtil; +import org.dbflute.mail.send.hook.SMailCallbackContext; import org.lastaflute.core.mail.Postbox; public class PingEsJob { @@ -37,23 +41,40 @@ public class PingEsJob { final StringBuilder resultBuf = new StringBuilder(); - final String notificationTo = fessConfig.getNotificationTo(); final PingResponse ping = fessEsClient.ping(); final int status = ping.getStatus(); if (systemHelper.isChangedClusterState(status)) { - if (StringUtil.isNotBlank(notificationTo)) { + if (fessConfig.hasNotification()) { + final String toStrs = fessConfig.getNotificationTo(); + final String[] toAddresses; + if (StringUtil.isNotBlank(toStrs)) { + toAddresses = toStrs.split(","); + } else { + toAddresses = StringUtil.EMPTY_STRINGS; + } final Postbox postbox = ComponentUtil.getComponent(Postbox.class); try { + final NotificationHelper notificationHelper = ComponentUtil.getNotificationHelper(); + SMailCallbackContext.setPreparedMessageHookOnThread(notificationHelper::send); EsStatusPostcard.droppedInto(postbox, postcard -> { postcard.setFrom(fessConfig.getMailFromAddress(), fessConfig.getMailFromName()); postcard.addReplyTo(fessConfig.getMailReturnPath()); - postcard.addTo(notificationTo); + if (toAddresses.length > 0) { + stream(toAddresses).of(stream -> stream.map(String::trim).forEach(address -> { + postcard.addTo(address); + })); + } else { + postcard.addTo(fessConfig.getMailFromAddress()); + postcard.dryrun(); + } postcard.setHostname(systemHelper.getHostname()); postcard.setClustername(ping.getClusterName()); postcard.setClusterstatus(ping.getClusterStatus()); }); } catch (final Exception e) { logger.warn("Failed to send a test mail.", e); + } finally { + SMailCallbackContext.clearPreparedMessageHookOnThread(); } } resultBuf.append("Status of ").append(ping.getClusterName()).append(" is changed to ").append(ping.getClusterStatus()) diff --git a/src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java b/src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java index 2cc74da9b..f8788c8b1 100644 --- a/src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java +++ b/src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java @@ -501,6 +501,18 @@ public interface FessProp { return getSystemProperty(Constants.PURGE_BY_BOTS_PROPERTY, Constants.DEFAULT_PURGE_BY_BOTS); } + default boolean hasNotification() { + return StringUtil.isNotBlank(getNotificationTo()) || StringUtil.isNotBlank(getSlackWebhookUrls()); + } + + default void getSlackWebhookUrls(final String value) { + setSystemProperty(Constants.SLACK_WEBHOOK_URLS_PROPERTY, value); + } + + default String getSlackWebhookUrls() { + return getSystemProperty(Constants.SLACK_WEBHOOK_URLS_PROPERTY, StringUtil.EMPTY); + } + default void setNotificationTo(final String value) { setSystemProperty(Constants.NOTIFICATION_TO_PROPERTY, value); } diff --git a/src/main/java/org/codelibs/fess/mylasta/direction/sponsor/FessMailDeliveryDepartmentCreator.java b/src/main/java/org/codelibs/fess/mylasta/direction/sponsor/FessMailDeliveryDepartmentCreator.java index 5c1b75f71..218c61def 100644 --- a/src/main/java/org/codelibs/fess/mylasta/direction/sponsor/FessMailDeliveryDepartmentCreator.java +++ b/src/main/java/org/codelibs/fess/mylasta/direction/sponsor/FessMailDeliveryDepartmentCreator.java @@ -82,7 +82,6 @@ public class FessMailDeliveryDepartmentCreator { } protected SMailDogmaticPostalPersonnel createDogmaticPostalPersonnel() { // #ext_point e.g. locale, database - final String testPrefix = fessConfig.getMailSubjectTestPrefix(); final AsyncManager asyncManager = getAsyncManager(); final MessageManager messageManager = getMessageManager(); return new SMailDogmaticPostalPersonnel() { @@ -96,7 +95,7 @@ public class FessMailDeliveryDepartmentCreator { @Override protected OptionalThing createSubjectFilter() { - return OptionalThing.of((view, subject) -> testPrefix + subject); + return OptionalThing.of((view, subject) -> subject); } @Override diff --git a/src/main/java/org/codelibs/fess/util/ComponentUtil.java b/src/main/java/org/codelibs/fess/util/ComponentUtil.java index cfff41631..3629f0c91 100644 --- a/src/main/java/org/codelibs/fess/util/ComponentUtil.java +++ b/src/main/java/org/codelibs/fess/util/ComponentUtil.java @@ -51,6 +51,7 @@ import org.codelibs.fess.helper.JobHelper; import org.codelibs.fess.helper.KeyMatchHelper; import org.codelibs.fess.helper.LabelTypeHelper; import org.codelibs.fess.helper.LanguageHelper; +import org.codelibs.fess.helper.NotificationHelper; import org.codelibs.fess.helper.PathMappingHelper; import org.codelibs.fess.helper.PermissionHelper; import org.codelibs.fess.helper.PluginHelper; @@ -93,6 +94,8 @@ public final class ComponentUtil { private static Map componentMap = new HashMap<>(); + private static final String NOTIFICATION_HELPER = "notificationHelper"; + private static final String SEARCH_HELPER = "searchHelper"; private static final String THEME_HELPER = "themeHelper"; @@ -467,6 +470,10 @@ public final class ComponentUtil { return getComponent(SEARCH_HELPER); } + public static NotificationHelper getNotificationHelper() { + return getComponent(NOTIFICATION_HELPER); + } + public static T getComponent(final Class clazz) { try { return SingletonLaContainer.getComponent(clazz); diff --git a/src/main/resources/fess.xml b/src/main/resources/fess.xml index 9b048700f..13bf7e442 100644 --- a/src/main/resources/fess.xml +++ b/src/main/resources/fess.xml @@ -31,6 +31,8 @@ + +