1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.job;
17
18 import static org.codelibs.core.stream.StreamUtil.stream;
19
20 import java.io.File;
21 import java.util.ArrayList;
22 import java.util.List;
23
24 import org.apache.commons.lang3.RandomStringUtils;
25 import org.apache.commons.lang3.SystemUtils;
26 import org.apache.logging.log4j.LogManager;
27 import org.apache.logging.log4j.Logger;
28 import org.codelibs.core.lang.StringUtil;
29 import org.codelibs.core.timer.TimeoutTask;
30 import org.codelibs.fess.Constants;
31 import org.codelibs.fess.exception.JobProcessingException;
32 import org.codelibs.fess.exec.ThumbnailGenerator;
33 import org.codelibs.fess.helper.ProcessHelper;
34 import org.codelibs.fess.mylasta.direction.FessConfig;
35 import org.codelibs.fess.util.ComponentUtil;
36 import org.codelibs.fess.util.InputStreamThread;
37 import org.codelibs.fess.util.JobProcess;
38 import org.codelibs.fess.util.ResourceUtil;
39 import org.codelibs.fess.util.SystemUtil;
40
41 import jakarta.servlet.ServletContext;
42
43
44
45
46
47
48 public class GenerateThumbnailJob extends ExecJob {
49
50 static final Logger logger = LogManager.getLogger(GenerateThumbnailJob.class);
51
52
53 protected int numOfThreads = 1;
54
55
56 protected boolean cleanup = false;
57
58
59
60
61 public GenerateThumbnailJob() {
62 super();
63 }
64
65
66
67
68
69
70
71 public GenerateThumbnailJob numOfThreads(final int numOfThreads) {
72 this.numOfThreads = numOfThreads;
73 return this;
74 }
75
76
77
78
79
80
81 public GenerateThumbnailJob cleanup() {
82 cleanup = true;
83 return this;
84 }
85
86 @Override
87 public String execute() {
88 final StringBuilder resultBuf = new StringBuilder();
89
90 if (sessionId == null) {
91 sessionId = RandomStringUtils.randomAlphabetic(15);
92 }
93 resultBuf.append("Session Id: ").append(sessionId).append("\n");
94 if (jobExecutor != null) {
95 jobExecutor.addShutdownListener(() -> ComponentUtil.getProcessHelper().destroyProcess(sessionId));
96 }
97
98 final TimeoutTask timeoutTask = createTimeoutTask();
99 try {
100 executeThumbnailGenerator();
101 } catch (final Exception e) {
102 logger.warn("Failed to generate thumbnails.", e);
103 resultBuf.append(e.getMessage()).append("\n");
104 } finally {
105 if (timeoutTask != null && !timeoutTask.isCanceled()) {
106 timeoutTask.cancel();
107 }
108 }
109
110 return resultBuf.toString();
111
112 }
113
114
115
116
117
118
119
120
121 protected void executeThumbnailGenerator() {
122 final List<String> cmdList = new ArrayList<>();
123 final String cpSeparator = SystemUtils.IS_OS_WINDOWS ? ";" : ":";
124 final ServletContext servletContext = ComponentUtil.getComponent(ServletContext.class);
125 final ProcessHelper processHelper = ComponentUtil.getProcessHelper();
126 final FessConfig fessConfig = ComponentUtil.getFessConfig();
127
128 cmdList.add(fessConfig.getJavaCommandPath());
129
130
131 cmdList.add("-cp");
132 final StringBuilder buf = new StringBuilder(100);
133 ResourceUtil.getOverrideConfPath().ifPresent(p -> {
134 buf.append(p);
135 buf.append(cpSeparator);
136 });
137 final String confPath = System.getProperty(Constants.FESS_CONF_PATH);
138 if (StringUtil.isNotBlank(confPath)) {
139 buf.append(confPath);
140 buf.append(cpSeparator);
141 }
142
143 buf.append("WEB-INF");
144 buf.append(File.separator);
145 buf.append("env");
146 buf.append(File.separator);
147 buf.append(getExecuteType());
148 buf.append(File.separator);
149 buf.append("resources");
150 buf.append(cpSeparator);
151
152 buf.append("WEB-INF");
153 buf.append(File.separator);
154 buf.append("classes");
155
156 final String userDir = System.getProperty("user.dir");
157 final File targetDir = new File(userDir, "target");
158 final File targetClassesDir = new File(targetDir, "classes");
159 if (targetClassesDir.isDirectory()) {
160 buf.append(cpSeparator);
161 buf.append(targetClassesDir.getAbsolutePath());
162 }
163
164 appendJarFile(cpSeparator, buf, new File(servletContext.getRealPath("/WEB-INF/lib")),
165 "WEB-INF" + File.separator + "lib" + File.separator);
166
167 appendJarFile(cpSeparator, buf, new File(servletContext.getRealPath("/WEB-INF/env/" + getExecuteType() + "/lib")),
168 "WEB-INF" + File.separator + "env" + File.separator + getExecuteType() + File.separator + "lib" + File.separator);
169
170 appendJarFile(cpSeparator, buf, new File(servletContext.getRealPath("/WEB-INF/plugin")),
171 "WEB-INF" + File.separator + "plugin" + File.separator);
172 final File targetLibDir = new File(targetDir, "fess" + File.separator + "WEB-INF" + File.separator + "lib");
173 if (targetLibDir.isDirectory()) {
174 appendJarFile(cpSeparator, buf, targetLibDir, targetLibDir.getAbsolutePath() + File.separator);
175 }
176 cmdList.add(buf.toString());
177
178 if (useLocalFesen) {
179 final String httpAddress = SystemUtil.getSearchEngineHttpAddress();
180 if (StringUtil.isNotBlank(httpAddress)) {
181 cmdList.add("-D" + Constants.FESS_SEARCH_ENGINE_HTTP_ADDRESS + "=" + httpAddress);
182 }
183 }
184
185 final String systemLastaEnv = System.getProperty("lasta.env");
186 if (StringUtil.isNotBlank(systemLastaEnv)) {
187 if ("web".equals(systemLastaEnv)) {
188 cmdList.add("-Dlasta.env=" + getExecuteType());
189 } else {
190 cmdList.add("-Dlasta.env=" + systemLastaEnv);
191 }
192 } else if (StringUtil.isNotBlank(lastaEnv)) {
193 cmdList.add("-Dlasta.env=" + lastaEnv);
194 } else {
195 cmdList.add("-Dlasta.env=" + getExecuteType());
196 }
197
198 addFessConfigProperties(cmdList);
199 addFessSystemProperties(cmdList);
200 addFessCustomSystemProperties(cmdList, fessConfig.getJobSystemPropertyFilterPattern());
201 addSystemProperty(cmdList, Constants.FESS_CONF_PATH, null, null);
202 cmdList.add("-Dfess." + getExecuteType() + ".process=true");
203 if (logFilePath == null) {
204 final String value = System.getProperty("fess.log.path");
205 logFilePath = value != null ? value : new File(targetDir, "logs").getAbsolutePath();
206 }
207 cmdList.add("-Dfess.log.path=" + logFilePath);
208 addSystemProperty(cmdList, Constants.FESS_VAR_PATH, null, null);
209 addSystemProperty(cmdList, Constants.FESS_THUMBNAIL_PATH, null, null);
210 addSystemProperty(cmdList, "fess.log.name", getLogName("fess"), getLogName(StringUtil.EMPTY));
211 if (logLevel != null) {
212 cmdList.add("-Dfess.log.level=" + logLevel);
213 }
214 stream(fessConfig.getJvmThumbnailOptionsAsArray())
215 .of(stream -> stream.filter(StringUtil::isNotBlank).forEach(value -> cmdList.add(value)));
216
217 File ownTmpDir = null;
218 final String tmpDir = System.getProperty("java.io.tmpdir");
219 if (fessConfig.isUseOwnTmpDir() && StringUtil.isNotBlank(tmpDir)) {
220 ownTmpDir = new File(tmpDir, "fessTmpDir_" + sessionId);
221 if (ownTmpDir.mkdirs()) {
222 cmdList.add("-Djava.io.tmpdir=" + ownTmpDir.getAbsolutePath());
223 } else {
224 ownTmpDir = null;
225 }
226 }
227
228 if (!jvmOptions.isEmpty()) {
229 jvmOptions.stream().filter(StringUtil::isNotBlank).forEach(cmdList::add);
230 }
231
232 cmdList.add(ThumbnailGenerator.class.getCanonicalName());
233
234 cmdList.add("--sessionId");
235 cmdList.add(sessionId);
236 cmdList.add("--numOfThreads");
237 cmdList.add(Integer.toString(numOfThreads));
238 if (cleanup) {
239 cmdList.add("--cleanup");
240 }
241
242 final File propFile = ComponentUtil.getSystemHelper().createTempFile(getExecuteType() + "_", ".properties");
243 try {
244 cmdList.add("-p");
245 cmdList.add(propFile.getAbsolutePath());
246 createSystemProperties(cmdList, propFile);
247
248 final File baseDir = new File(servletContext.getRealPath("/WEB-INF")).getParentFile();
249
250 if (logger.isInfoEnabled()) {
251 logger.info("ThumbnailGenerator: \nDirectory={}\nOptions={}", baseDir, cmdList);
252 }
253
254 final JobProcess jobProcess = processHelper.startProcess(sessionId, cmdList, pb -> {
255 pb.directory(baseDir);
256 pb.redirectErrorStream(true);
257 });
258
259 final InputStreamThread it = jobProcess.getInputStreamThread();
260 it.start();
261
262 final Process currentProcess = jobProcess.getProcess();
263 currentProcess.waitFor();
264 it.join(5000);
265
266 final int exitValue = currentProcess.exitValue();
267
268 if (logger.isInfoEnabled()) {
269 logger.info("ThumbnailGenerator: Exit Code={} - Process Output:\n{}", exitValue, it.getOutput());
270 }
271 if (exitValue != 0) {
272 final StringBuilder out = new StringBuilder();
273 if (processTimeout) {
274 out.append("Process is terminated due to ").append(timeout).append(" second exceeded.\n");
275 }
276 out.append("Exit Code: ").append(exitValue).append("\nOutput:\n").append(it.getOutput());
277 throw new JobProcessingException(out.toString());
278 }
279 ComponentUtil.getPopularWordHelper().clearCache();
280 } catch (final JobProcessingException e) {
281 throw e;
282 } catch (final Exception e) {
283 throw new JobProcessingException("ThumbnailGenerator Process terminated.", e);
284 } finally {
285 try {
286 processHelper.destroyProcess(sessionId);
287 } finally {
288 if (propFile != null && !propFile.delete()) {
289 logger.warn("Failed to delete properties file: {}", propFile.getAbsolutePath());
290 }
291 deleteTempDir(ownTmpDir);
292 }
293 }
294 }
295
296 @Override
297 protected String getExecuteType() {
298 return Constants.EXECUTE_TYPE_THUMBNAIL;
299 }
300 }