1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.helper;
17
18 import static org.codelibs.core.stream.StreamUtil.split;
19 import static org.codelibs.core.stream.StreamUtil.stream;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.UnsupportedEncodingException;
25 import java.net.InetAddress;
26 import java.net.URLEncoder;
27 import java.net.UnknownHostException;
28 import java.nio.file.Files;
29 import java.nio.file.Path;
30 import java.time.Instant;
31 import java.time.LocalDateTime;
32 import java.time.ZoneId;
33 import java.util.ArrayList;
34 import java.util.Calendar;
35 import java.util.Collections;
36 import java.util.Date;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.LinkedHashMap;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.Map;
43 import java.util.Map.Entry;
44 import java.util.Properties;
45 import java.util.Set;
46 import java.util.TimeZone;
47 import java.util.UUID;
48 import java.util.concurrent.ExecutionException;
49 import java.util.concurrent.TimeUnit;
50 import java.util.concurrent.atomic.AtomicBoolean;
51 import java.util.concurrent.atomic.AtomicInteger;
52 import java.util.function.Supplier;
53 import java.util.regex.Pattern;
54 import java.util.stream.Collectors;
55
56 import org.apache.commons.lang3.LocaleUtils;
57 import org.apache.commons.lang3.StringUtils;
58 import org.apache.logging.log4j.Level;
59 import org.apache.logging.log4j.LogManager;
60 import org.apache.logging.log4j.Logger;
61 import org.apache.logging.log4j.core.config.Configurator;
62 import org.codelibs.core.exception.IORuntimeException;
63 import org.codelibs.core.lang.StringUtil;
64 import org.codelibs.core.lang.ThreadUtil;
65 import org.codelibs.core.misc.Pair;
66 import org.codelibs.core.timer.TimeoutManager;
67 import org.codelibs.core.timer.TimeoutTask;
68 import org.codelibs.fess.Constants;
69 import org.codelibs.fess.crawler.util.CharUtil;
70 import org.codelibs.fess.exception.FessSystemException;
71 import org.codelibs.fess.mylasta.action.FessMessages;
72 import org.codelibs.fess.mylasta.action.FessUserBean;
73 import org.codelibs.fess.mylasta.direction.FessConfig;
74 import org.codelibs.fess.timer.LoadControlMonitorTarget;
75 import org.codelibs.fess.util.ComponentUtil;
76 import org.codelibs.fess.util.GsaConfigParser;
77 import org.codelibs.fess.util.IpAddressUtil;
78 import org.codelibs.fess.util.ParameterUtil;
79 import org.codelibs.fess.util.ResourceUtil;
80 import org.codelibs.fess.validation.FessActionValidator;
81 import org.lastaflute.core.message.supplier.UserMessagesCreator;
82 import org.lastaflute.web.TypicalAction;
83 import org.lastaflute.web.response.HtmlResponse;
84 import org.lastaflute.web.ruts.process.ActionRuntime;
85 import org.lastaflute.web.servlet.request.RequestManager;
86 import org.lastaflute.web.util.LaServletContextUtil;
87 import org.lastaflute.web.validation.ActionValidator;
88 import org.opensearch.monitor.os.OsProbe;
89
90 import com.google.common.cache.CacheBuilder;
91 import com.google.common.cache.CacheLoader;
92 import com.google.common.cache.LoadingCache;
93 import com.ibm.icu.util.ULocale;
94
95 import jakarta.annotation.PostConstruct;
96 import jakarta.annotation.PreDestroy;
97
98
99
100
101
102
103 public class SystemHelper {
104
105
106
107
108 public SystemHelper() {
109
110 }
111
112 private static final Logger logger = LogManager.getLogger(SystemHelper.class);
113
114
115 protected final Map<String, String> designJspFileNameMap = new LinkedHashMap<>();
116
117
118 protected final AtomicBoolean forceStop = new AtomicBoolean(false);
119
120
121 protected LoadingCache<String, List<Map<String, String>>> langItemsCache;
122
123
124 protected String filterPathEncoding;
125
126
127 protected String[] supportedLanguages;
128
129
130 protected List<Runnable> shutdownHookList = new ArrayList<>();
131
132
133 protected AtomicInteger previousClusterState = new AtomicInteger(0);
134
135
136 protected String version;
137
138
139 protected int majorVersion;
140
141
142 protected int minorVersion;
143
144
145 protected String productVersion;
146
147
148 protected long eolTime;
149
150 private short systemCpuPercent;
151
152 private long systemCpuCheckTime;
153
154 private long systemCpuCheckInterval = 1000L;
155
156 private volatile short searchEngineCpuPercent;
157
158 private volatile TimeoutTask loadControlMonitorTask;
159
160
161 protected Map<String, Supplier<String>> updateConfigListenerMap = new HashMap<>();
162
163
164 protected Set<String> waitingThreadNames = Collections.synchronizedSet(new HashSet<>());
165
166
167
168
169
170 @PostConstruct
171 public void init() {
172 if (logger.isDebugEnabled()) {
173 logger.debug("Initializing {}", this.getClass().getSimpleName());
174 }
175 final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
176 cal.set(2027, 10 - 1, 1);
177 eolTime = cal.getTimeInMillis();
178 if (isEoled()) {
179 logger.error("Your system is out of support. See https://fess.codelibs.org/eol.html");
180 }
181 updateSystemProperties();
182 final FessConfig fessConfig = ComponentUtil.getFessConfig();
183 filterPathEncoding = fessConfig.getPathEncoding();
184 supportedLanguages = fessConfig.getSupportedLanguagesAsArray();
185 langItemsCache = CacheBuilder.newBuilder()
186 .maximumSize(20)
187 .expireAfterAccess(1, TimeUnit.HOURS)
188 .build(new CacheLoader<String, List<Map<String, String>>>() {
189 @Override
190 public List<Map<String, String>> load(final String key) throws Exception {
191 final ULocale uLocale = new ULocale(key);
192 final Locale displayLocale = uLocale.toLocale();
193 final List<Map<String, String>> langItems = new ArrayList<>(supportedLanguages.length);
194 final String msg = ComponentUtil.getMessageManager().getMessage(displayLocale, "labels.allLanguages");
195 final Map<String, String> defaultMap = new HashMap<>(2);
196 defaultMap.put(Constants.ITEM_LABEL, msg);
197 defaultMap.put(Constants.ITEM_VALUE, "all");
198 langItems.add(defaultMap);
199
200 for (final String lang : supportedLanguages) {
201 final Locale locale = LocaleUtils.toLocale(lang);
202 final String label = locale.getDisplayName(displayLocale);
203 final Map<String, String> map = new HashMap<>(2);
204 map.put(Constants.ITEM_LABEL, label);
205 map.put(Constants.ITEM_VALUE, lang);
206 langItems.add(map);
207 }
208 return langItems;
209 }
210 });
211
212 ComponentUtil.doInitProcesses(Runnable::run);
213
214 parseProjectProperties(ResourceUtil.getProjectPropertiesFile());
215
216 updateConfigListenerMap.put("Label", () -> Integer.toString(ComponentUtil.getLabelTypeHelper().load()));
217 updateConfigListenerMap.put("PathMapping", () -> Integer.toString(ComponentUtil.getPathMappingHelper().load()));
218 updateConfigListenerMap.put("RelatedContent", () -> Integer.toString(ComponentUtil.getRelatedContentHelper().load()));
219 updateConfigListenerMap.put("RelatedQuery", () -> Integer.toString(ComponentUtil.getRelatedQueryHelper().load()));
220 updateConfigListenerMap.put("KeyMatch", () -> Integer.toString(ComponentUtil.getKeyMatchHelper().load()));
221 }
222
223
224
225
226
227
228
229 protected void parseProjectProperties(final Path propPath) {
230 try (final InputStream in = Files.newInputStream(propPath)) {
231 final Properties prop = new Properties();
232 prop.load(in);
233 version = prop.getProperty("fess.version", "0.0.0");
234 final String[] values = version.split("\\.");
235 majorVersion = Integer.parseInt(values[0]);
236 minorVersion = Integer.parseInt(values[1]);
237 productVersion = majorVersion + "." + minorVersion;
238 System.setProperty("fess.version", version);
239 System.setProperty("fess.product.version", productVersion);
240 } catch (final Exception e) {
241 throw new FessSystemException("Failed to parse project.properties.", e);
242 }
243 }
244
245
246
247
248 @PreDestroy
249 public void destroy() {
250 shutdownHookList.forEach(action -> {
251 try {
252 action.run();
253 } catch (final Exception e) {
254 logger.warn("Failed to process shutdown task.", e);
255 }
256 });
257 }
258
259
260
261
262
263
264 public String getUsername() {
265 return getRequestManager().findUserBean(FessUserBean.class).map(FessUserBean::getUserId).orElse(Constants.GUEST_USER);
266 }
267
268
269
270
271
272
273 protected RequestManager getRequestManager() {
274 return ComponentUtil.getRequestManager();
275 }
276
277
278
279
280
281
282 public Date getCurrentTime() {
283 return new Date(getCurrentTimeAsLong());
284 }
285
286
287
288
289
290
291 public long getCurrentTimeAsLong() {
292 return System.currentTimeMillis();
293 }
294
295
296
297
298
299
300 public LocalDateTime getCurrentTimeAsLocalDateTime() {
301 final Instant instant = Instant.ofEpochMilli(getCurrentTimeAsLong());
302 return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
303 }
304
305
306
307
308
309
310 public String getLogFilePath() {
311 final String value = System.getProperty("fess.log.path");
312 if (value != null) {
313 return value;
314 }
315 final String userDir = System.getProperty("user.dir");
316 final File targetDir = new File(userDir, "target");
317 return new File(targetDir, "logs").getAbsolutePath();
318 }
319
320
321
322
323
324
325
326 public String encodeUrlFilter(final String path) {
327 if (filterPathEncoding == null || path == null) {
328 return path;
329 }
330
331 try {
332 final StringBuilder buf = new StringBuilder(path.length() + 100);
333 for (int i = 0; i < path.length(); i++) {
334 final char c = path.charAt(i);
335 if (CharUtil.isUrlChar(c) || c == '^' || c == '{' || c == '}' || c == '|' || c == '\\') {
336 buf.append(c);
337 } else {
338 buf.append(URLEncoder.encode(String.valueOf(c), filterPathEncoding));
339 }
340 }
341 return buf.toString();
342 } catch (final UnsupportedEncodingException e) {
343 return path;
344 }
345 }
346
347
348
349
350
351
352
353 public String normalizeConfigPath(final String path) {
354
355 if (StringUtil.isBlank(path)) {
356 return StringUtils.EMPTY;
357 }
358
359 final String p = path.trim();
360 if (p.startsWith("#")) {
361 return StringUtils.EMPTY;
362 }
363
364 if (p.startsWith(GsaConfigParser.CONTAINS)) {
365 return ".*" + Pattern.quote(p.substring(GsaConfigParser.CONTAINS.length())) + ".*";
366 }
367
368 if (p.startsWith(GsaConfigParser.REGEXP)) {
369 return p.substring(GsaConfigParser.REGEXP.length());
370 }
371
372 if (p.startsWith(GsaConfigParser.REGEXP_CASE)) {
373 return p.substring(GsaConfigParser.REGEXP_CASE.length());
374 }
375
376 if (p.startsWith(GsaConfigParser.REGEXP_IGNORE_CASE)) {
377 return "(?i)" + p.substring(GsaConfigParser.REGEXP_IGNORE_CASE.length());
378 }
379
380 return p;
381 }
382
383
384
385
386
387
388 public String getForumLink() {
389 final String url = ComponentUtil.getFessConfig().getForumLink();
390 if (StringUtil.isBlank(url)) {
391 return null;
392 }
393 String target = null;
394 final Locale locale = ComponentUtil.getRequestManager().getUserLocale();
395 if (locale != null) {
396 final String lang = locale.getLanguage();
397 if (ComponentUtil.getFessConfig().isOnlineHelpSupportedLang(lang)) {
398 target = lang.toUpperCase(Locale.ROOT);
399 }
400 }
401 return url.replaceFirst("\\{lang\\}", target == null ? "EN" : target);
402 }
403
404
405
406
407
408
409
410 public String getHelpLink(final String name) {
411 final String url = ComponentUtil.getFessConfig().getOnlineHelpBaseLink() + name + "-guide.html";
412 return getHelpUrl(url);
413 }
414
415
416
417
418
419
420
421 protected String getHelpUrl(final String url) {
422 final Locale locale = ComponentUtil.getRequestManager().getUserLocale();
423 if (locale != null) {
424 final String lang = locale.getLanguage();
425 if (ComponentUtil.getFessConfig().isOnlineHelpSupportedLang(lang)) {
426 return url.replaceFirst("\\{lang\\}", lang).replaceFirst("\\{version\\}", majorVersion + "." + minorVersion);
427 }
428 }
429 return getDefaultHelpLink(url);
430 }
431
432
433
434
435
436
437
438 protected String getDefaultHelpLink(final String url) {
439 return url.replaceFirst("/\\{lang\\}/", "/").replaceFirst("\\{version\\}", majorVersion + "." + minorVersion);
440 }
441
442
443
444
445
446
447
448 public void addDesignJspFileName(final String key, final String value) {
449 designJspFileNameMap.put(key, value);
450 }
451
452
453
454
455
456
457
458 public String getDesignJspFileName(final String fileName) {
459 return designJspFileNameMap.get(fileName);
460 }
461
462
463
464
465
466
467 @SuppressWarnings("unchecked")
468 public Pair<String, String>[] getDesignJspFileNames() {
469 return designJspFileNameMap.entrySet().stream().map(e -> new Pair<>(e.getKey(), e.getValue())).toArray(n -> new Pair[n]);
470 }
471
472
473
474
475
476
477 public List<Path> refreshDesignJspFiles() {
478 final List<Path> fileList = new ArrayList<>();
479 stream(ComponentUtil.getVirtualHostHelper().getVirtualHostPaths())
480 .of(stream -> stream.filter(s -> s != null && !"/".equals(s)).forEach(key -> {
481 designJspFileNameMap.entrySet().stream().forEach(e -> {
482 final File jspFile = getDesignJspFile("/WEB-INF/view" + key + "/" + e.getValue());
483 if (!jspFile.exists()) {
484 jspFile.getParentFile().mkdirs();
485 final File baseJspFile = getDesignJspFile("/WEB-INF/view/" + e.getValue());
486 try {
487 final Path jspPath = jspFile.toPath();
488 Files.copy(baseJspFile.toPath(), jspPath);
489 fileList.add(jspPath);
490 } catch (final IOException ex) {
491 logger.warn("Could not copy from {} to {}", baseJspFile.getAbsolutePath(), jspFile.getAbsolutePath(), ex);
492 }
493 }
494 });
495 }));
496 return fileList;
497 }
498
499
500
501
502
503
504
505 protected File getDesignJspFile(final String path) {
506 return new File(LaServletContextUtil.getServletContext().getRealPath(path));
507 }
508
509
510
511
512
513
514 public boolean isForceStop() {
515 return forceStop.get();
516 }
517
518
519
520
521
522
523 public void setForceStop(final boolean b) {
524 forceStop.set(b);
525 }
526
527
528
529
530
531
532
533 public String generateDocId(final Map<String, Object> map) {
534 return UUID.randomUUID().toString().replace("-", StringUtil.EMPTY);
535 }
536
537
538
539
540
541
542
543 public String abbreviateLongText(final String str) {
544 return StringUtils.abbreviate(str, ComponentUtil.getFessConfig().getMaxLogOutputLengthAsInteger());
545 }
546
547
548
549
550
551
552
553 public String normalizeHtmlLang(final String value) {
554 final String defaultLang = ComponentUtil.getFessConfig().getCrawlerDocumentHtmlDefaultLang();
555 if (StringUtil.isNotBlank(defaultLang)) {
556 return defaultLang;
557 }
558
559 return normalizeLang(value);
560 }
561
562
563
564
565
566
567
568 public String normalizeLang(final String value) {
569 if (StringUtil.isBlank(value)) {
570 return null;
571 }
572
573 final String localeName = value.trim().toLowerCase(Locale.ENGLISH).replace("-", "_");
574
575 for (final String supportedLang : supportedLanguages) {
576 if (localeName.startsWith(supportedLang.toLowerCase(Locale.ENGLISH))) {
577 return supportedLang;
578 }
579 }
580 return null;
581 }
582
583
584
585
586
587
588
589 public List<Map<String, String>> getLanguageItems(final Locale locale) {
590 try {
591 final String localeStr = locale.toString();
592 return langItemsCache.get(localeStr);
593 } catch (final ExecutionException e) {
594 final List<Map<String, String>> langItems = new ArrayList<>(supportedLanguages.length);
595 final String msg = ComponentUtil.getMessageManager().getMessage(locale, "labels.allLanguages");
596 final Map<String, String> defaultMap = new HashMap<>(2);
597 defaultMap.put(Constants.ITEM_LABEL, msg);
598 defaultMap.put(Constants.ITEM_VALUE, "all");
599 langItems.add(defaultMap);
600 return langItems;
601 }
602 }
603
604
605
606
607
608
609 public void addShutdownHook(final Runnable hook) {
610 shutdownHookList.add(hook);
611 }
612
613
614
615
616
617
618 public String getHostname() {
619 final Map<String, String> env = getEnvMap();
620 if (env.containsKey("COMPUTERNAME")) {
621 return env.get("COMPUTERNAME");
622 }
623 if (env.containsKey("HOSTNAME")) {
624 return env.get("HOSTNAME");
625 }
626 try {
627 return IpAddressUtil.getUrlHost(InetAddress.getLocalHost());
628 } catch (final UnknownHostException e) {
629 logger.debug("Unknown hostname.", e);
630 }
631 return "Unknown";
632 }
633
634
635
636
637
638
639
640
641 public String getInstanceId() {
642 final String targetName = ComponentUtil.getFessConfig().getSchedulerTargetName();
643 final String hostname = getHostname();
644 final long pid = ProcessHandle.current().pid();
645 if (StringUtil.isNotBlank(targetName)) {
646 return targetName + "@" + hostname + ":" + pid;
647 }
648 return hostname + ":" + pid;
649 }
650
651
652
653
654
655
656
657 public void setupAdminHtmlData(final TypicalAction action, final ActionRuntime runtime) {
658 runtime.registerData("developmentMode", ComponentUtil.getSearchEngineClient().isEmbedded());
659 final FessConfig fessConfig = ComponentUtil.getFessConfig();
660 final String installationLink = fessConfig.getOnlineHelpInstallation();
661 runtime.registerData("installationLink", getHelpUrl(installationLink));
662 runtime.registerData("storageEnabled",
663 StringUtil.isNotBlank(fessConfig.getStorageEndpoint()) && StringUtil.isNotBlank(fessConfig.getStorageBucket()));
664 final boolean eoled = isEoled();
665 runtime.registerData("eoled", eoled);
666 if (eoled) {
667 final String eolLink = fessConfig.getOnlineHelpEol();
668 runtime.registerData("eolLink", getHelpUrl(eolLink));
669 }
670 }
671
672
673
674
675
676
677
678 public void setupSearchHtmlData(final TypicalAction action, final ActionRuntime runtime) {
679 runtime.registerData("developmentMode", ComponentUtil.getSearchEngineClient().isEmbedded());
680 final FessConfig fessConfig = ComponentUtil.getFessConfig();
681 final String installationLink = fessConfig.getOnlineHelpInstallation();
682 runtime.registerData("installationLink", getHelpUrl(installationLink));
683 final boolean eoled = isEoled();
684 runtime.registerData("eoled", eoled);
685 if (eoled) {
686 final String eolLink = fessConfig.getOnlineHelpEol();
687 runtime.registerData("eolLink", getHelpUrl(eolLink));
688 }
689 }
690
691
692
693
694
695
696 protected boolean isEoled() {
697 return getCurrentTimeAsLong() > eolTime;
698 }
699
700
701
702
703
704
705
706 public boolean isUserPermission(final String permission) {
707 if (StringUtil.isNotBlank(permission)) {
708 return permission.startsWith(ComponentUtil.getFessConfig().getRoleSearchUserPrefix());
709 }
710 return false;
711 }
712
713
714
715
716
717
718
719 public String getSearchRoleByUser(final String name) {
720 return createSearchRole(ComponentUtil.getFessConfig().getRoleSearchUserPrefix(), name);
721 }
722
723
724
725
726
727
728
729 public String getSearchRoleByGroup(final String name) {
730 return createSearchRole(ComponentUtil.getFessConfig().getRoleSearchGroupPrefix(), name);
731 }
732
733
734
735
736
737
738
739 public String getSearchRoleByRole(final String name) {
740 return createSearchRole(ComponentUtil.getFessConfig().getRoleSearchRolePrefix(), name);
741 }
742
743
744
745
746
747
748
749
750 protected String createSearchRole(final String type, final String name) {
751 final String value = type + ComponentUtil.getFessConfig().getCanonicalLdapName(name);
752 if (logger.isDebugEnabled()) {
753 logger.debug("Search Role: {}:{}={}", type, name, value);
754 }
755 return value;
756 }
757
758
759
760
761 public void reloadConfiguration() {
762 reloadConfiguration(true);
763 }
764
765
766
767
768
769
770 public void reloadConfiguration(final boolean resetJobs) {
771 ComponentUtil.getSearchEngineClient().refresh();
772
773 ComponentUtil.getSuggestHelper().init();
774 ComponentUtil.getPopularWordHelper().init();
775
776 ComponentUtil.getLabelTypeHelper().update();
777 ComponentUtil.getPathMappingHelper().update();
778 ComponentUtil.getRelatedContentHelper().update();
779 ComponentUtil.getRelatedQueryHelper().update();
780 ComponentUtil.getKeyMatchHelper().update();
781
782 ComponentUtil.getLdapManager().updateConfig();
783 if (resetJobs) {
784 ComponentUtil.getJobManager().reboot();
785 }
786 updateSystemProperties();
787
788 ComponentUtil.getRankFusionProcessor().update();
789 }
790
791
792
793
794 public void updateSystemProperties() {
795 final String value = ComponentUtil.getFessConfig().getAppValue();
796 if (logger.isDebugEnabled()) {
797 logger.debug("system.properties: {}", value);
798 }
799 if (StringUtil.isNotBlank(value)) {
800 ParameterUtil.parse(ParameterUtil.encrypt(value)).entrySet().stream().filter(e -> {
801 final String key = e.getKey();
802 if (StringUtil.isBlank(key)) {
803 return false;
804 }
805 if (key.startsWith("fess.")) {
806 return true;
807 }
808 return System.getProperty(key) == null;
809 }).forEach(e -> {
810 if (logger.isDebugEnabled()) {
811 logger.debug("system.properties: setProperty({}, {})", e.getKey(), e.getValue());
812 }
813 System.setProperty(e.getKey(), e.getValue());
814 });
815 }
816 }
817
818
819
820
821
822
823 public String updateConfiguration() {
824 final StringBuilder buf = new StringBuilder();
825 updateConfigListenerMap.entrySet().stream().forEach(e -> {
826 buf.append(e.getKey()).append(": ");
827 try {
828 buf.append(e.getValue().get());
829 } catch (final Exception ex) {
830 logger.warn("Failed to process {} task.", e.getKey(), ex);
831 buf.append(ex.getMessage());
832 }
833 buf.append('\n');
834 });
835 return buf.toString();
836 }
837
838
839
840
841
842
843
844 public void addUpdateConfigListener(final String name, final Supplier<String> listener) {
845 updateConfigListenerMap.put(name, listener);
846 }
847
848
849
850
851
852
853
854 public boolean isChangedClusterState(final int status) {
855 return previousClusterState.getAndSet(status) != status;
856 }
857
858
859
860
861
862
863
864
865
866 public ActionValidator<FessMessages> createValidator(final RequestManager requestManager,
867 final UserMessagesCreator<FessMessages> messagesCreator, final Class<?>[] runtimeGroups) {
868 return new FessActionValidator<>(requestManager, messagesCreator, runtimeGroups);
869 }
870
871
872
873
874
875
876
877 public HtmlResponse getRedirectResponseToLogin(final HtmlResponse response) {
878 return response;
879 }
880
881
882
883
884
885
886
887 public HtmlResponse getRedirectResponseToRoot(final HtmlResponse response) {
888 return response;
889 }
890
891
892
893
894
895
896 public void setLogLevel(final String level) {
897 final Level logLevel = Level.toLevel(level, Level.WARN);
898 System.setProperty(Constants.FESS_LOG_LEVEL, logLevel.toString());
899 split(ComponentUtil.getFessConfig().getLoggingAppPackages(), ",")
900 .of(stream -> stream.map(String::trim).filter(StringUtil::isNotEmpty).forEach(s -> Configurator.setLevel(s, logLevel)));
901 }
902
903
904
905
906
907
908 public String getLogLevel() {
909 return System.getProperty(Constants.FESS_LOG_LEVEL, Level.WARN.toString());
910 }
911
912 private static final String[] LLM_LOG_PACKAGES =
913 { "org.codelibs.fess.llm", "org.codelibs.fess.chat", "org.codelibs.fess.api.chat", "org.codelibs.fess.app.web.chat" };
914
915
916
917
918
919
920 public void setLlmLogLevel(final String level) {
921 final Level logLevel = Level.toLevel(level, Level.INFO);
922 System.setProperty(Constants.FESS_LLM_LOG_LEVEL, logLevel.toString());
923 for (final String pkg : LLM_LOG_PACKAGES) {
924 Configurator.setLevel(pkg, logLevel);
925 }
926 }
927
928
929
930
931
932
933 public String getLlmLogLevel() {
934 return System.getProperty(Constants.FESS_LLM_LOG_LEVEL, Level.INFO.toString());
935 }
936
937
938
939
940
941
942
943
944
945 public File createTempFile(final String prefix, final String suffix) {
946 try {
947 final File file = File.createTempFile(prefix, suffix);
948 file.setReadable(false, false);
949 file.setReadable(true, true);
950 file.setWritable(false, false);
951 file.setWritable(true, true);
952 if (logger.isDebugEnabled()) {
953 logger.debug("Create {} as a temp file.", file.getAbsolutePath());
954 }
955 return file;
956 } catch (final IOException e) {
957 throw new IORuntimeException(e);
958 }
959 }
960
961
962
963
964
965
966 public boolean calibrateCpuLoad() {
967 return calibrateCpuLoad(0L);
968 }
969
970
971
972
973
974
975
976 public boolean calibrateCpuLoad(final long timeoutInMillis) {
977 final short percent = ComponentUtil.getFessConfig().getAdaptiveLoadControlAsInteger().shortValue();
978 if (percent <= 0) {
979 return true;
980 }
981 short current = getSystemCpuPercent();
982 if (current < percent) {
983 return true;
984 }
985 final long startTime = getCurrentTimeAsLong();
986 final String threadName = Thread.currentThread().getName();
987 try {
988 waitingThreadNames.add(threadName);
989 while (current >= percent) {
990 if (timeoutInMillis > 0 && getCurrentTimeAsLong() - startTime > timeoutInMillis) {
991 if (logger.isInfoEnabled()) {
992 logger.info("Cpu Load {}% is greater than {}%. {} waiting thread(s). {} thread is timed out.", current, percent,
993 waitingThreadNames.size(), threadName);
994 }
995 return false;
996 }
997 if (logger.isInfoEnabled()) {
998 logger.info("Cpu Load {}% is greater than {}%. {} waiting thread(s).", current, percent, waitingThreadNames.size());
999 }
1000 if (logger.isDebugEnabled()) {
1001 logger.debug("Waiting threads: {}", waitingThreadNames);
1002 }
1003 ThreadUtil.sleep(systemCpuCheckInterval);
1004 current = getSystemCpuPercent();
1005 }
1006 } finally {
1007 waitingThreadNames.remove(threadName);
1008 }
1009 return true;
1010 }
1011
1012
1013
1014
1015 public void waitForNoWaitingThreads() {
1016 int count = waitingThreadNames.size();
1017 while (count > 0) {
1018 if (logger.isInfoEnabled()) {
1019 logger.info("{} waiting thread(s).", count);
1020 }
1021 ThreadUtil.sleep(systemCpuCheckInterval);
1022 count = waitingThreadNames.size();
1023 }
1024 }
1025
1026
1027
1028
1029
1030
1031 protected short getSystemCpuPercent() {
1032 final long now = getCurrentTimeAsLong();
1033 if (now - systemCpuCheckTime > systemCpuCheckInterval) {
1034 synchronized (this) {
1035 if (now - systemCpuCheckTime > systemCpuCheckInterval) {
1036 try {
1037 final OsProbe osProbe = OsProbe.getInstance();
1038 systemCpuPercent = osProbe.getSystemCpuPercent();
1039 if (logger.isDebugEnabled()) {
1040 logger.debug("Updated System Cpu {}%", systemCpuPercent);
1041 }
1042 } catch (final Exception e) {
1043 logger.warn("Failed to get SystemCpuPercent.", e);
1044 return 0;
1045 }
1046 systemCpuCheckTime = now;
1047 }
1048 }
1049 }
1050 return systemCpuPercent;
1051 }
1052
1053
1054
1055
1056
1057
1058 public short currentSystemCpuPercent() {
1059 return getSystemCpuPercent();
1060 }
1061
1062
1063
1064
1065
1066
1067 public short getSearchEngineCpuPercent() {
1068 ensureLoadControlMonitorStarted();
1069 return searchEngineCpuPercent;
1070 }
1071
1072
1073
1074
1075
1076
1077 public void setSearchEngineCpuPercent(final short percent) {
1078 searchEngineCpuPercent = percent;
1079 }
1080
1081 private void ensureLoadControlMonitorStarted() {
1082 if (loadControlMonitorTask == null) {
1083 final FessConfig fessConfig = ComponentUtil.getFessConfig();
1084 if (fessConfig.getWebLoadControlAsInteger() < 100 || fessConfig.getApiLoadControlAsInteger() < 100) {
1085 synchronized (this) {
1086 if (loadControlMonitorTask == null) {
1087 final int interval = fessConfig.getLoadControlMonitorIntervalAsInteger();
1088 loadControlMonitorTask =
1089 TimeoutManager.getInstance().addTimeoutTarget(new LoadControlMonitorTarget(this), interval, true);
1090 }
1091 }
1092 }
1093 }
1094 }
1095
1096
1097
1098
1099
1100
1101
1102 public Map<String, String> getFilteredEnvMap(final String keyPattern) {
1103 final Pattern pattern = Pattern.compile(keyPattern);
1104 return getEnvMap().entrySet().stream().filter(e -> {
1105 final String key = e.getKey();
1106 if (StringUtil.isBlank(key)) {
1107 return false;
1108 }
1109 return pattern.matcher(key).matches();
1110 }).collect(Collectors.toMap(Entry<String, String>::getKey, Entry<String, String>::getValue));
1111 }
1112
1113
1114
1115
1116
1117
1118 protected Map<String, String> getEnvMap() {
1119 return System.getenv();
1120 }
1121
1122
1123
1124
1125
1126
1127 public String getVersion() {
1128 return version;
1129 }
1130
1131
1132
1133
1134
1135
1136 public int getMajorVersion() {
1137 return majorVersion;
1138 }
1139
1140
1141
1142
1143
1144
1145 public int getMinorVersion() {
1146 return minorVersion;
1147 }
1148
1149
1150
1151
1152
1153
1154 public String getProductVersion() {
1155 return productVersion;
1156 }
1157
1158
1159
1160
1161
1162
1163 public void setSystemCpuCheckInterval(final long systemCpuCheckInterval) {
1164 this.systemCpuCheckInterval = systemCpuCheckInterval;
1165 }
1166
1167
1168
1169
1170
1171
1172
1173 public String validatePassword(final String password) {
1174 if (StringUtil.isBlank(password)) {
1175 return "errors.blank_password";
1176 }
1177
1178 final FessConfig fessConfig = ComponentUtil.getFessConfig();
1179
1180 final Integer minLength = fessConfig.getPasswordMinLengthAsInteger();
1181 if (minLength != null && minLength > 0 && password.length() < minLength) {
1182 return "errors.password_length";
1183 }
1184
1185 if (fessConfig.isPasswordRequireUppercase() && !containsUppercase(password)) {
1186 return "errors.password_no_uppercase";
1187 }
1188
1189 if (fessConfig.isPasswordRequireLowercase() && !containsLowercase(password)) {
1190 return "errors.password_no_lowercase";
1191 }
1192
1193 if (fessConfig.isPasswordRequireDigit() && !containsDigit(password)) {
1194 return "errors.password_no_digit";
1195 }
1196
1197 if (fessConfig.isPasswordRequireSpecialChar() && !containsSpecialChar(password)) {
1198 return "errors.password_no_special_char";
1199 }
1200
1201 if (!fessConfig.isValidAdminPassword(password)) {
1202 return "errors.password_is_blacklisted";
1203 }
1204
1205 return StringUtil.EMPTY;
1206 }
1207
1208
1209
1210
1211
1212
1213
1214 protected boolean containsUppercase(final String password) {
1215 for (int i = 0; i < password.length(); i++) {
1216 if (Character.isUpperCase(password.charAt(i))) {
1217 return true;
1218 }
1219 }
1220 return false;
1221 }
1222
1223
1224
1225
1226
1227
1228
1229 protected boolean containsLowercase(final String password) {
1230 for (int i = 0; i < password.length(); i++) {
1231 if (Character.isLowerCase(password.charAt(i))) {
1232 return true;
1233 }
1234 }
1235 return false;
1236 }
1237
1238
1239
1240
1241
1242
1243
1244 protected boolean containsDigit(final String password) {
1245 for (int i = 0; i < password.length(); i++) {
1246 if (Character.isDigit(password.charAt(i))) {
1247 return true;
1248 }
1249 }
1250 return false;
1251 }
1252
1253
1254
1255
1256
1257
1258
1259
1260 protected boolean containsSpecialChar(final String password) {
1261 for (int i = 0; i < password.length(); i++) {
1262 final char c = password.charAt(i);
1263 if (!Character.isLetterOrDigit(c) && !Character.isWhitespace(c)) {
1264 return true;
1265 }
1266 }
1267 return false;
1268 }
1269 }