View Javadoc
1   /*
2    * Copyright 2012-2021 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
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 javax.servlet.ServletContext;
25  
26  import org.apache.commons.lang3.RandomStringUtils;
27  import org.apache.commons.lang3.SystemUtils;
28  import org.apache.logging.log4j.LogManager;
29  import org.apache.logging.log4j.Logger;
30  import org.codelibs.core.lang.StringUtil;
31  import org.codelibs.core.timer.TimeoutTask;
32  import org.codelibs.fess.Constants;
33  import org.codelibs.fess.exception.JobProcessingException;
34  import org.codelibs.fess.exec.ThumbnailGenerator;
35  import org.codelibs.fess.helper.ProcessHelper;
36  import org.codelibs.fess.mylasta.direction.FessConfig;
37  import org.codelibs.fess.util.ComponentUtil;
38  import org.codelibs.fess.util.InputStreamThread;
39  import org.codelibs.fess.util.JobProcess;
40  import org.codelibs.fess.util.ResourceUtil;
41  
42  public class GenerateThumbnailJob extends ExecJob {
43      static final Logger logger = LogManager.getLogger(GenerateThumbnailJob.class);
44  
45      protected int numOfThreads = 1;
46  
47      protected boolean cleanup = false;
48  
49      public GenerateThumbnailJob numOfThreads(final int numOfThreads) {
50          this.numOfThreads = numOfThreads;
51          return this;
52      }
53  
54      public GenerateThumbnailJob cleanup() {
55          this.cleanup = true;
56          return this;
57      }
58  
59      @Override
60      public String execute() {
61          final StringBuilder resultBuf = new StringBuilder();
62  
63          if (sessionId == null) { // create session id
64              sessionId = RandomStringUtils.randomAlphabetic(15);
65          }
66          resultBuf.append("Session Id: ").append(sessionId).append("\n");
67          if (jobExecutor != null) {
68              jobExecutor.addShutdownListener(() -> ComponentUtil.getProcessHelper().destroyProcess(sessionId));
69          }
70  
71          final TimeoutTask timeoutTask = createTimeoutTask();
72          try {
73              executeThumbnailGenerator();
74          } catch (final Exception e) {
75              logger.warn("Failed to generate thumbnails.", e);
76              resultBuf.append(e.getMessage()).append("\n");
77          } finally {
78              if (timeoutTask != null && !timeoutTask.isCanceled()) {
79                  timeoutTask.cancel();
80              }
81          }
82  
83          return resultBuf.toString();
84  
85      }
86  
87      protected void executeThumbnailGenerator() {
88          final List<String> cmdList = new ArrayList<>();
89          final String cpSeparator = SystemUtils.IS_OS_WINDOWS ? ";" : ":";
90          final ServletContext servletContext = ComponentUtil.getComponent(ServletContext.class);
91          final ProcessHelper processHelper = ComponentUtil.getProcessHelper();
92          final FessConfig fessConfig = ComponentUtil.getFessConfig();
93  
94          cmdList.add(fessConfig.getJavaCommandPath());
95  
96          // -cp
97          cmdList.add("-cp");
98          final StringBuilder buf = new StringBuilder(100);
99          ResourceUtil.getOverrideConfPath().ifPresent(p -> {
100             buf.append(p);
101             buf.append(cpSeparator);
102         });
103         final String confPath = System.getProperty(Constants.FESS_CONF_PATH);
104         if (StringUtil.isNotBlank(confPath)) {
105             buf.append(confPath);
106             buf.append(cpSeparator);
107         }
108         // WEB-INF/env/thumbnail/resources
109         buf.append("WEB-INF");
110         buf.append(File.separator);
111         buf.append("env");
112         buf.append(File.separator);
113         buf.append(getExecuteType());
114         buf.append(File.separator);
115         buf.append("resources");
116         buf.append(cpSeparator);
117         // WEB-INF/classes
118         buf.append("WEB-INF");
119         buf.append(File.separator);
120         buf.append("classes");
121         // target/classes
122         final String userDir = System.getProperty("user.dir");
123         final File targetDir = new File(userDir, "target");
124         final File targetClassesDir = new File(targetDir, "classes");
125         if (targetClassesDir.isDirectory()) {
126             buf.append(cpSeparator);
127             buf.append(targetClassesDir.getAbsolutePath());
128         }
129         // WEB-INF/lib
130         appendJarFile(cpSeparator, buf, new File(servletContext.getRealPath("/WEB-INF/lib")),
131                 "WEB-INF" + File.separator + "lib" + File.separator);
132         // WEB-INF/env/thumbnail/lib
133         appendJarFile(cpSeparator, buf, new File(servletContext.getRealPath("/WEB-INF/env/" + getExecuteType() + "/lib")),
134                 "WEB-INF" + File.separator + "env" + File.separator + getExecuteType() + File.separator + "lib" + File.separator);
135         // WEB-INF/plugin
136         appendJarFile(cpSeparator, buf, new File(servletContext.getRealPath("/WEB-INF/plugin")),
137                 "WEB-INF" + File.separator + "plugin" + File.separator);
138         final File targetLibDir = new File(targetDir, "fess" + File.separator + "WEB-INF" + File.separator + "lib");
139         if (targetLibDir.isDirectory()) {
140             appendJarFile(cpSeparator, buf, targetLibDir, targetLibDir.getAbsolutePath() + File.separator);
141         }
142         cmdList.add(buf.toString());
143 
144         if (useLocalFesen) {
145             final String httpAddress = System.getProperty(Constants.FESS_ES_HTTP_ADDRESS);
146             if (StringUtil.isNotBlank(httpAddress)) {
147                 cmdList.add("-D" + Constants.FESS_ES_HTTP_ADDRESS + "=" + httpAddress);
148             }
149         }
150 
151         final String systemLastaEnv = System.getProperty("lasta.env");
152         if (StringUtil.isNotBlank(systemLastaEnv)) {
153             if ("web".equals(systemLastaEnv)) {
154                 cmdList.add("-Dlasta.env=" + getExecuteType());
155             } else {
156                 cmdList.add("-Dlasta.env=" + systemLastaEnv);
157             }
158         } else if (StringUtil.isNotBlank(lastaEnv)) {
159             cmdList.add("-Dlasta.env=" + lastaEnv);
160         } else {
161             cmdList.add("-Dlasta.env=" + getExecuteType());
162         }
163 
164         addFessConfigProperties(cmdList);
165         addFessSystemProperties(cmdList);
166         addSystemProperty(cmdList, Constants.FESS_CONF_PATH, null, null);
167         cmdList.add("-Dfess." + getExecuteType() + ".process=true");
168         if (logFilePath == null) {
169             final String value = System.getProperty("fess.log.path");
170             logFilePath = value != null ? value : new File(targetDir, "logs").getAbsolutePath();
171         }
172         cmdList.add("-Dfess.log.path=" + logFilePath);
173         addSystemProperty(cmdList, Constants.FESS_VAR_PATH, null, null);
174         addSystemProperty(cmdList, Constants.FESS_THUMBNAIL_PATH, null, null);
175         addSystemProperty(cmdList, "fess.log.name", "fess-" + getExecuteType(), "-" + getExecuteType());
176         if (logLevel != null) {
177             cmdList.add("-Dfess.log.level=" + logLevel);
178         }
179         stream(fessConfig.getJvmThumbnailOptionsAsArray())
180                 .of(stream -> stream.filter(StringUtil::isNotBlank).forEach(value -> cmdList.add(value)));
181 
182         File ownTmpDir = null;
183         final String tmpDir = System.getProperty("java.io.tmpdir");
184         if (fessConfig.isUseOwnTmpDir() && StringUtil.isNotBlank(tmpDir)) {
185             ownTmpDir = new File(tmpDir, "fessTmpDir_" + sessionId);
186             if (ownTmpDir.mkdirs()) {
187                 cmdList.add("-Djava.io.tmpdir=" + ownTmpDir.getAbsolutePath());
188             } else {
189                 ownTmpDir = null;
190             }
191         }
192 
193         if (!jvmOptions.isEmpty()) {
194             jvmOptions.stream().filter(StringUtil::isNotBlank).forEach(cmdList::add);
195         }
196 
197         cmdList.add(ThumbnailGenerator.class.getCanonicalName());
198 
199         cmdList.add("--sessionId");
200         cmdList.add(sessionId);
201         cmdList.add("--numOfThreads");
202         cmdList.add(Integer.toString(numOfThreads));
203         if (cleanup) {
204             cmdList.add("--cleanup");
205         }
206 
207         final File propFile = ComponentUtil.getSystemHelper().createTempFile(getExecuteType() + "_", ".properties");
208         try {
209             cmdList.add("-p");
210             cmdList.add(propFile.getAbsolutePath());
211             createSystemProperties(cmdList, propFile);
212 
213             final File baseDir = new File(servletContext.getRealPath("/WEB-INF")).getParentFile();
214 
215             if (logger.isInfoEnabled()) {
216                 logger.info("ThumbnailGenerator: \nDirectory={}\nOptions={}", baseDir, cmdList);
217             }
218 
219             final JobProcess jobProcess = processHelper.startProcess(sessionId, cmdList, pb -> {
220                 pb.directory(baseDir);
221                 pb.redirectErrorStream(true);
222             });
223 
224             final InputStreamThread it = jobProcess.getInputStreamThread();
225             it.start();
226 
227             final Process currentProcess = jobProcess.getProcess();
228             currentProcess.waitFor();
229             it.join(5000);
230 
231             final int exitValue = currentProcess.exitValue();
232 
233             if (logger.isInfoEnabled()) {
234                 logger.info("ThumbnailGenerator: Exit Code={} - Process Output:\n{}", exitValue, it.getOutput());
235             }
236             if (exitValue != 0) {
237                 final StringBuilder out = new StringBuilder();
238                 if (processTimeout) {
239                     out.append("Process is terminated due to ").append(timeout).append(" second exceeded.\n");
240                 }
241                 out.append("Exit Code: ").append(exitValue).append("\nOutput:\n").append(it.getOutput());
242                 throw new JobProcessingException(out.toString());
243             }
244             ComponentUtil.getPopularWordHelper().clearCache();
245         } catch (final JobProcessingException e) {
246             throw e;
247         } catch (final Exception e) {
248             throw new JobProcessingException("ThumbnailGenerator Process terminated.", e);
249         } finally {
250             try {
251                 processHelper.destroyProcess(sessionId);
252             } finally {
253                 if (propFile != null && !propFile.delete()) {
254                     logger.warn("Failed to delete {}.", propFile.getAbsolutePath());
255                 }
256                 deleteTempDir(ownTmpDir);
257             }
258         }
259     }
260 
261     @Override
262     protected String getExecuteType() {
263         return Constants.EXECUTE_TYPE_THUMBNAIL;
264     }
265 }