1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.thumbnail.impl;
17
18 import java.io.BufferedReader;
19 import java.io.File;
20 import java.io.InputStreamReader;
21 import java.nio.charset.Charset;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Timer;
25 import java.util.TimerTask;
26
27 import javax.annotation.PostConstruct;
28 import javax.annotation.Resource;
29 import javax.servlet.ServletContext;
30
31 import org.apache.commons.io.IOUtils;
32 import org.codelibs.core.io.CopyUtil;
33 import org.codelibs.core.lang.StringUtil;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 public class CommandGenerator extends BaseThumbnailGenerator {
38 private static final Logger logger = LoggerFactory.getLogger(CommandGenerator.class);
39
40 @Resource
41 protected ServletContext application;
42
43 protected List<String> commandList;
44
45 protected long commandTimeout = 30 * 1000L;
46
47 protected File baseDir;
48
49 private volatile Timer destoryTimer;
50
51 @PostConstruct
52 public void init() {
53 if (baseDir == null) {
54 baseDir = new File(application.getRealPath("/"));
55 }
56 destoryTimer = new Timer("CommandGeneratorDestoryTimer-" + System.currentTimeMillis(), true);
57 }
58
59 @Override
60 public void destroy() {
61 destoryTimer.cancel();
62 destoryTimer = null;
63 }
64
65 @Override
66 public boolean generate(final String thumbnailId, final File outputFile) {
67 if (logger.isDebugEnabled()) {
68 logger.debug("Generate Thumbnail: " + thumbnailId);
69 }
70
71 if (outputFile.exists()) {
72 if (logger.isDebugEnabled()) {
73 logger.debug("The thumbnail file exists: " + outputFile.getAbsolutePath());
74 }
75 return true;
76 }
77
78 final File parentFile = outputFile.getParentFile();
79 if (!parentFile.exists()) {
80 parentFile.mkdirs();
81 }
82 if (!parentFile.isDirectory()) {
83 logger.warn("Not found: " + parentFile.getAbsolutePath());
84 return false;
85 }
86
87 return process(thumbnailId, responseData -> {
88 File tempFile = null;
89 try {
90 tempFile = File.createTempFile("thumbnail_", "");
91 CopyUtil.copy(responseData.getResponseBody(), tempFile);
92
93 final String tempPath = tempFile.getAbsolutePath();
94 final String outputPath = outputFile.getAbsolutePath();
95 final List<String> cmdList = new ArrayList<>();
96 for (final String value : commandList) {
97 cmdList.add(expandPath(value.replace("${url}", tempPath).replace("${outputFile}", outputPath)));
98 }
99
100 executeCommand(thumbnailId, cmdList);
101
102 if (outputFile.isFile() && outputFile.length() == 0) {
103 logger.warn("Thumbnail File is empty. ID is " + thumbnailId);
104 if (outputFile.delete()) {
105 logger.info("Deleted: " + outputFile.getAbsolutePath());
106 }
107 updateThumbnailField(thumbnailId, StringUtil.EMPTY);
108 return false;
109 }
110
111 if (logger.isDebugEnabled()) {
112 logger.debug("Thumbnail File: " + outputPath);
113 }
114 return true;
115 } catch (final Exception e) {
116 logger.warn("Failed to process ", e);
117 if (tempFile != null && !tempFile.delete()) {
118 logger.debug("Failed to delete " + tempFile.getAbsolutePath());
119 }
120 updateThumbnailField(thumbnailId, StringUtil.EMPTY);
121 return false;
122 }
123 });
124
125 }
126
127 protected void executeCommand(final String thumbnailId, final List<String> cmdList) {
128 ProcessBuilder pb = null;
129 Process p = null;
130
131 if (logger.isDebugEnabled()) {
132 logger.debug("Thumbnail Command: " + cmdList);
133 }
134
135 TimerTask task = null;
136 try {
137 pb = new ProcessBuilder(cmdList);
138 pb.directory(baseDir);
139 pb.redirectErrorStream(true);
140
141 p = pb.start();
142
143 task = new ProcessDestroyer(p, cmdList);
144 try {
145 destoryTimer.schedule(task, commandTimeout);
146
147 String line;
148 BufferedReader br = null;
149 try {
150 br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.defaultCharset()));
151 while ((line = br.readLine()) != null) {
152 if (logger.isDebugEnabled()) {
153 logger.debug(line);
154 }
155 }
156 } finally {
157 IOUtils.closeQuietly(br);
158 }
159
160 p.waitFor();
161 } catch (final Exception e) {
162 p.destroy();
163 }
164 } catch (final Exception e) {
165 logger.warn("Failed to generate a thumbnail of " + thumbnailId, e);
166 }
167 if (task != null) {
168 task.cancel();
169 task = null;
170 }
171 }
172
173 protected static class ProcessDestroyer extends TimerTask {
174
175 private final Process p;
176
177 private final List<String> commandList;
178
179 protected ProcessDestroyer(final Process p, final List<String> commandList) {
180 this.p = p;
181 this.commandList = commandList;
182 }
183
184 @Override
185 public void run() {
186 logger.warn("CommandGenerator is timed out: " + commandList);
187 try {
188 p.destroy();
189 } catch (final Exception e) {
190 logger.warn("Failed to stop destroyer.", e);
191 }
192 }
193 }
194
195 public void setCommandList(final List<String> commandList) {
196 this.commandList = commandList;
197 }
198
199 public void setCommandTimeout(final long commandTimeout) {
200 this.commandTimeout = commandTimeout;
201 }
202
203 public void setBaseDir(final File baseDir) {
204 this.baseDir = baseDir;
205 }
206
207 }