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 import java.util.Map;
24
25 import org.apache.commons.lang3.RandomStringUtils;
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.helper.ProcessHelper;
33 import org.codelibs.fess.mylasta.direction.FessConfig;
34 import org.codelibs.fess.util.ComponentUtil;
35 import org.codelibs.fess.util.InputStreamThread;
36 import org.codelibs.fess.util.JobProcess;
37 import org.codelibs.fess.util.SystemUtil;
38
39 import jakarta.servlet.ServletContext;
40
41
42
43
44
45
46
47
48
49 public class PythonJob extends ExecJob {
50
51 static final Logger logger = LogManager.getLogger(PythonJob.class);
52
53
54
55
56
57 public PythonJob() {
58 super();
59 }
60
61
62 protected String filename;
63
64
65 protected List<String> argList = new ArrayList<>();
66
67
68
69
70
71
72
73 public PythonJob filename(final String filename) {
74 this.filename = filename;
75 return this;
76 }
77
78
79
80
81
82
83
84 public PythonJob arg(final String value) {
85 argList.add(value);
86 return this;
87 }
88
89
90
91
92
93
94
95 public PythonJob args(final String... values) {
96 stream(values).of(stream -> stream.forEach(argList::add));
97 return this;
98 }
99
100
101
102
103
104
105
106
107 @Override
108 public String execute() {
109 final StringBuilder resultBuf = new StringBuilder();
110
111 if (sessionId == null) {
112 sessionId = RandomStringUtils.randomAlphabetic(15);
113 }
114 resultBuf.append("Session Id: ").append(sessionId).append("\n");
115 if (jobExecutor != null) {
116 jobExecutor.addShutdownListener(() -> ComponentUtil.getProcessHelper().destroyProcess(sessionId));
117 }
118
119 final TimeoutTask timeoutTask = createTimeoutTask();
120 try {
121 executePython();
122 } catch (final Exception e) {
123 logger.warn("Failed to run python command.", e);
124 resultBuf.append(e.getMessage()).append("\n");
125 } finally {
126 if (timeoutTask != null && !timeoutTask.isCanceled()) {
127 timeoutTask.cancel();
128 }
129 }
130
131 return resultBuf.toString();
132
133 }
134
135
136
137
138
139
140
141
142 protected void executePython() {
143 final List<String> cmdList = new ArrayList<>();
144 final ServletContext servletContext = ComponentUtil.getComponent(ServletContext.class);
145 final ProcessHelper processHelper = ComponentUtil.getProcessHelper();
146 final FessConfig fessConfig = ComponentUtil.getFessConfig();
147
148 if (StringUtil.isBlank(filename)) {
149 throw new JobProcessingException("Python script is not specified.");
150 }
151
152 cmdList.add(fessConfig.getPythonCommandPath());
153
154 cmdList.add(getPyFilePath());
155
156 cmdList.addAll(argList);
157
158 try {
159
160 final File baseDir = new File(servletContext.getRealPath("/WEB-INF")).getParentFile();
161
162 if (logger.isInfoEnabled()) {
163 logger.info("Python: \nDirectory={}\nOptions={}", baseDir, cmdList);
164 }
165
166 final JobProcess jobProcess = processHelper.startProcess(sessionId, cmdList, pb -> {
167 pb.directory(baseDir);
168 pb.redirectErrorStream(true);
169 final Map<String, String> environment = pb.environment();
170 environment.put("SESSION_ID", sessionId);
171 environment.put("OPENSEARCH_URL", SystemUtil.getSearchEngineHttpAddress());
172 });
173
174 final InputStreamThread it = jobProcess.getInputStreamThread();
175 it.start();
176
177 final Process currentProcess = jobProcess.getProcess();
178 currentProcess.waitFor();
179 it.join(5000);
180
181 final int exitValue = currentProcess.exitValue();
182
183 if (logger.isInfoEnabled()) {
184 logger.info("Python: Exit Code={} - Process Output:\n{}", exitValue, it.getOutput());
185 }
186 if (exitValue != 0) {
187 final StringBuilder out = new StringBuilder();
188 if (processTimeout) {
189 out.append("Process is terminated due to ").append(timeout).append(" second exceeded.\n");
190 }
191 out.append("Exit Code: ").append(exitValue).append("\nOutput:\n").append(it.getOutput());
192 throw new JobProcessingException(out.toString());
193 }
194 ComponentUtil.getPopularWordHelper().clearCache();
195 } catch (final JobProcessingException e) {
196 throw e;
197 } catch (final Exception e) {
198 throw new JobProcessingException("Python Process terminated.", e);
199 } finally {
200 processHelper.destroyProcess(sessionId);
201
202 }
203 }
204
205
206
207
208
209
210
211
212 protected String getPyFilePath() {
213 final StringBuilder buf = new StringBuilder(100);
214 buf.append("WEB-INF");
215 buf.append(File.separator);
216 buf.append("env");
217 buf.append(File.separator);
218 buf.append(getExecuteType());
219 buf.append(File.separator);
220 buf.append("resources");
221 buf.append(File.separator);
222 buf.append(filename.replaceAll("\\.\\.+", ""));
223 return buf.toString();
224 }
225
226
227
228
229
230
231 @Override
232 protected String getExecuteType() {
233 return Constants.EXECUTE_TYPE_PYTHON;
234 }
235 }