1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.exec;
17
18 import java.io.File;
19 import java.lang.management.ManagementFactory;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import java.util.concurrent.TimeUnit;
23
24 import org.apache.logging.log4j.LogManager;
25 import org.apache.logging.log4j.Logger;
26 import org.codelibs.core.lang.StringUtil;
27 import org.codelibs.core.misc.DynamicProperties;
28 import org.codelibs.core.timer.TimeoutManager;
29 import org.codelibs.core.timer.TimeoutTask;
30 import org.codelibs.fess.Constants;
31 import org.codelibs.fess.crawler.client.FesenClient;
32 import org.codelibs.fess.exception.ContainerNotAvailableException;
33 import org.codelibs.fess.opensearch.client.SearchEngineClient;
34 import org.codelibs.fess.timer.LogNotificationTarget;
35 import org.codelibs.fess.timer.SystemMonitorTarget;
36 import org.codelibs.fess.util.ComponentUtil;
37 import org.codelibs.fess.util.SystemUtil;
38 import org.kohsuke.args4j.CmdLineException;
39 import org.kohsuke.args4j.CmdLineParser;
40 import org.kohsuke.args4j.Option;
41 import org.lastaflute.di.core.external.GenericExternalContext;
42 import org.lastaflute.di.core.external.GenericExternalContextComponentDefRegister;
43 import org.lastaflute.di.core.factory.SingletonLaContainerFactory;
44 import org.opensearch.monitor.jvm.JvmInfo;
45 import org.opensearch.monitor.os.OsProbe;
46 import org.opensearch.monitor.process.ProcessProbe;
47
48 import jakarta.annotation.Resource;
49
50
51
52
53
54 public class ThumbnailGenerator {
55
56 private static final Logger logger = LogManager.getLogger(ThumbnailGenerator.class);
57
58
59
60
61 @Resource
62 public SearchEngineClient searchEngineClient;
63
64
65
66
67 public ThumbnailGenerator() {
68
69 }
70
71
72
73
74 protected static class Options {
75
76
77
78 @Option(name = "-s", aliases = "--sessionId", metaVar = "sessionId", usage = "Session ID")
79 protected String sessionId;
80
81
82
83
84 @Option(name = "-n", aliases = "--name", metaVar = "name", usage = "Name")
85 protected String name;
86
87
88
89
90 @Option(name = "-p", aliases = "--properties", metaVar = "properties", usage = "Properties File")
91 protected String propertiesPath;
92
93
94
95
96 @Option(name = "-t", aliases = "--numOfThreads", metaVar = "numOfThreads", usage = "The number of threads")
97 protected int numOfThreads = 1;
98
99
100
101
102 @Option(name = "-c", aliases = "--cleanup", usage = "Clean-Up mode")
103 protected boolean cleanup;
104
105
106
107
108 protected Options() {
109
110 }
111
112 @Override
113 public String toString() {
114 return "Options [sessionId=" + sessionId + ", name=" + name + ", propertiesPath=" + propertiesPath + ", numOfThreads="
115 + numOfThreads + "]";
116 }
117
118 }
119
120 static void initializeProbes() {
121
122 ProcessProbe.getInstance();
123 OsProbe.getInstance();
124 JvmInfo.jvmInfo();
125 }
126
127
128
129
130
131
132 public static void main(final String[] args) {
133 final Options options = new Options();
134
135 final CmdLineParser parser = new CmdLineParser(options);
136 try {
137 parser.parseArgument(args);
138 } catch (final CmdLineException e) {
139 System.err.println(e.getMessage());
140 System.err.println("java " + ThumbnailGenerator.class.getCanonicalName() + " [options...] arguments...");
141 parser.printUsage(System.err);
142 return;
143 }
144
145 if (logger.isDebugEnabled()) {
146 try {
147 ManagementFactory.getRuntimeMXBean().getInputArguments().stream().forEach(s -> logger.debug("Parameter: {}", s));
148 System.getProperties()
149 .entrySet()
150 .stream()
151 .forEach(e -> logger.debug("Property: {}={}", e.getKey(),
152 SystemUtil.maskSensitiveValue(String.valueOf(e.getKey()), String.valueOf(e.getValue()))));
153 System.getenv()
154 .entrySet()
155 .forEach(e -> logger.debug("Env: {}={}", e.getKey(), SystemUtil.maskSensitiveValue(e.getKey(), e.getValue())));
156 logger.debug("Options: options={}", options);
157 } catch (final Exception e) {
158
159 }
160 }
161
162 final String httpAddress = SystemUtil.getSearchEngineHttpAddress();
163 if (StringUtil.isNotBlank(httpAddress)) {
164 System.setProperty(FesenClient.HTTP_ADDRESS, httpAddress);
165 }
166
167 TimeoutTask systemMonitorTask = null;
168 TimeoutTask logNotificationTask = null;
169 LogNotificationTarget logNotificationTarget = null;
170 int exitCode;
171 try {
172 SingletonLaContainerFactory.setConfigPath("app.xml");
173 SingletonLaContainerFactory.setExternalContext(new GenericExternalContext());
174 SingletonLaContainerFactory.setExternalContextComponentDefRegister(new GenericExternalContextComponentDefRegister());
175 SingletonLaContainerFactory.init();
176
177 final Thread shutdownCallback = new Thread("ShutdownHook") {
178 @Override
179 public void run() {
180 if (logger.isDebugEnabled()) {
181 logger.debug("Destroying LaContainer...");
182 }
183 destroyContainer();
184 }
185 };
186 Runtime.getRuntime().addShutdownHook(shutdownCallback);
187
188 systemMonitorTask = TimeoutManager.getInstance()
189 .addTimeoutTarget(new SystemMonitorTarget(), ComponentUtil.getFessConfig().getThumbnailSystemMonitorIntervalAsInteger(),
190 true);
191
192 if (ComponentUtil.getFessConfig().isLogNotificationEnabled()) {
193 logNotificationTarget = new LogNotificationTarget();
194 logNotificationTask = TimeoutManager.getInstance()
195 .addTimeoutTarget(logNotificationTarget, ComponentUtil.getFessConfig().getLogNotificationFlushIntervalAsInteger(),
196 true);
197 }
198
199 final int totalCount = process(options);
200 if (totalCount != 0) {
201 logger.info("Processed {} thumbnail task(s).", totalCount);
202 } else {
203 logger.info("No thumbnail tasks to process.");
204 }
205 exitCode = 0;
206 } catch (final ContainerNotAvailableException e) {
207 if (logger.isDebugEnabled()) {
208 logger.debug("ThumbnailGenerator is stopped.", e);
209 } else if (logger.isInfoEnabled()) {
210 logger.info("ThumbnailGenerator is stopped.");
211 }
212 exitCode = Constants.EXIT_FAIL;
213 } catch (final Throwable t) {
214 logger.error("ThumbnailGenerator terminated unexpectedly.", t);
215 exitCode = Constants.EXIT_FAIL;
216 } finally {
217 if (systemMonitorTask != null) {
218 systemMonitorTask.cancel();
219 }
220 if (logNotificationTask != null) {
221 logNotificationTask.cancel();
222 }
223 if (logNotificationTarget != null) {
224 logNotificationTarget.flush();
225 }
226 destroyContainer();
227 }
228
229 System.exit(exitCode);
230 }
231
232 private static void destroyContainer() {
233 TimeoutManager.getInstance().stop();
234 synchronized (SingletonLaContainerFactory.class) {
235 SingletonLaContainerFactory.destroy();
236 }
237 }
238
239 private static int process(final Options options) {
240 final DynamicProperties systemProperties = ComponentUtil.getSystemProperties();
241
242 if (StringUtil.isNotBlank(options.propertiesPath)) {
243 systemProperties.reload(options.propertiesPath);
244 } else {
245 try {
246 final File propFile = ComponentUtil.getSystemHelper().createTempFile("thumbnail_", ".properties");
247 if (propFile.delete() && logger.isDebugEnabled()) {
248 logger.debug("Deleted temp file: path={}", propFile.getAbsolutePath());
249 }
250 systemProperties.reload(propFile.getAbsolutePath());
251 propFile.deleteOnExit();
252 } catch (final Exception e) {
253 logger.warn("Failed to create system properties file.", e);
254 }
255 }
256
257 int totalCount = 0;
258 int count = 1;
259 final ExecutorService executorService = Executors.newFixedThreadPool(options.numOfThreads);
260 try {
261 while (count != 0) {
262 count = ComponentUtil.getThumbnailManager().generate(executorService, options.cleanup);
263 totalCount += count;
264 }
265 executorService.shutdown();
266 executorService.awaitTermination(60, TimeUnit.SECONDS);
267 } catch (final InterruptedException e) {
268 if (logger.isDebugEnabled()) {
269 logger.debug("Interrupted.", e);
270 }
271 } finally {
272 executorService.shutdownNow();
273 }
274 return totalCount;
275 }
276 }