001
014
015 package com.liferay.portal.image;
016
017 import com.liferay.portal.kernel.configuration.Filter;
018 import com.liferay.portal.kernel.image.ImageMagick;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.security.pacl.DoPrivileged;
022 import com.liferay.portal.kernel.util.ClassLoaderUtil;
023 import com.liferay.portal.kernel.util.NamedThreadFactory;
024 import com.liferay.portal.kernel.util.OSDetector;
025 import com.liferay.portal.kernel.util.PropsKeys;
026 import com.liferay.portal.kernel.util.StringBundler;
027 import com.liferay.portal.kernel.util.Validator;
028 import com.liferay.portal.util.PrefsPropsUtil;
029 import com.liferay.portal.util.PropsUtil;
030
031 import java.util.LinkedList;
032 import java.util.List;
033 import java.util.Map;
034 import java.util.Properties;
035 import java.util.concurrent.Future;
036
037 import javax.portlet.PortletPreferences;
038
039 import org.im4java.process.ArrayListOutputConsumer;
040 import org.im4java.process.ProcessExecutor;
041 import org.im4java.process.ProcessTask;
042
043
047 @DoPrivileged
048 public class ImageMagickImpl implements ImageMagick {
049
050 public static ImageMagickImpl getInstance() {
051 return _instance;
052 }
053
054 @Override
055 public Future<?> convert(List<String> arguments) throws Exception {
056 if (!isEnabled()) {
057 throw new IllegalStateException(
058 "Cannot call \"convert\" when ImageMagick is disabled");
059 }
060
061 ProcessExecutor processExecutor = _getProcessExecutor();
062
063 LiferayConvertCmd liferayConvertCmd = new LiferayConvertCmd();
064
065 ProcessTask processTask = liferayConvertCmd.getProcessTask(
066 _globalSearchPath, getResourceLimits(), arguments);
067
068 processExecutor.execute(processTask);
069
070 return processTask;
071 }
072
073 @Override
074 public void destroy() {
075 if (_processExecutor == null) {
076 return;
077 }
078
079 synchronized (ProcessExecutor.class) {
080 _processExecutor.shutdownNow();
081 }
082
083 _processExecutor = null;
084 }
085
086 @Override
087 public String getGlobalSearchPath() throws Exception {
088 PortletPreferences preferences = PrefsPropsUtil.getPreferences(true);
089
090 String globalSearchPath = preferences.getValue(
091 PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, null);
092
093 if (Validator.isNotNull(globalSearchPath)) {
094 return globalSearchPath;
095 }
096
097 String filterName = null;
098
099 if (OSDetector.isApple()) {
100 filterName = "apple";
101 }
102 else if (OSDetector.isWindows()) {
103 filterName = "windows";
104 }
105 else {
106 filterName = "unix";
107 }
108
109 return PropsUtil.get(
110 PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, new Filter(filterName));
111 }
112
113 @Override
114 public Properties getResourceLimitsProperties() throws Exception {
115 Properties resourceLimitsProperties = PrefsPropsUtil.getProperties(
116 PropsKeys.IMAGEMAGICK_RESOURCE_LIMIT, true);
117
118 if (resourceLimitsProperties.isEmpty()) {
119 resourceLimitsProperties = PropsUtil.getProperties(
120 PropsKeys.IMAGEMAGICK_RESOURCE_LIMIT, true);
121 }
122
123 return resourceLimitsProperties;
124 }
125
126 @Override
127 public String[] identify(List<String> arguments) throws Exception {
128 if (!isEnabled()) {
129 throw new IllegalStateException(
130 "Cannot call \"identify\" when ImageMagick is disabled");
131 }
132
133 ProcessExecutor processExecutor = _getProcessExecutor();
134
135 LiferayIdentifyCmd liferayIdentifyCmd = new LiferayIdentifyCmd();
136
137 ArrayListOutputConsumer arrayListOutputConsumer =
138 new ArrayListOutputConsumer();
139
140 liferayIdentifyCmd.setOutputConsumer(arrayListOutputConsumer);
141
142 ProcessTask processTask = liferayIdentifyCmd.getProcessTask(
143 _globalSearchPath, getResourceLimits(), arguments);
144
145 processExecutor.execute(processTask);
146
147 processTask.get();
148
149 List<String> output = arrayListOutputConsumer.getOutput();
150
151 if (output != null) {
152 return output.toArray(new String[output.size()]);
153 }
154
155 return new String[0];
156 }
157
158 @Override
159 public boolean isEnabled() {
160 boolean enabled = false;
161
162 try {
163 enabled = PrefsPropsUtil.getBoolean(PropsKeys.IMAGEMAGICK_ENABLED);
164 }
165 catch (Exception e) {
166 if (_log.isWarnEnabled()) {
167 _log.warn(e, e);
168 }
169 }
170
171 if (!enabled && !_warned && _log.isWarnEnabled()) {
172 StringBundler sb = new StringBundler(7);
173
174 sb.append("Liferay is not configured to use ImageMagick and ");
175 sb.append("Ghostscript. For better quality document and image ");
176 sb.append("previews, install ImageMagick and Ghostscript. Enable ");
177 sb.append("ImageMagick in portal-ext.properties or in the Server ");
178 sb.append("Administration section of the Control Panel at: ");
179 sb.append("http:
180 sb.append("external-services");
181
182 _log.warn(sb.toString());
183
184 _warned = true;
185 }
186
187 return enabled;
188 }
189
190 @Override
191 public void reset() {
192 if (isEnabled()) {
193 try {
194 _globalSearchPath = getGlobalSearchPath();
195
196 _resourceLimitsProperties = getResourceLimitsProperties();
197 }
198 catch (Exception e) {
199 _log.error(e, e);
200 }
201 }
202 }
203
204 protected LinkedList<String> getResourceLimits() {
205 LinkedList<String> resourceLimits = new LinkedList<>();
206
207 if (_resourceLimitsProperties == null) {
208 return resourceLimits;
209 }
210
211 for (Map.Entry<Object, Object> entry :
212 _resourceLimitsProperties.entrySet()) {
213
214 String value = (String)entry.getValue();
215
216 if (Validator.isNull(value)) {
217 continue;
218 }
219
220 resourceLimits.add("-limit");
221 resourceLimits.add((String)entry.getKey());
222 resourceLimits.add(value);
223 }
224
225 return resourceLimits;
226 }
227
228 private ProcessExecutor _getProcessExecutor() {
229 if (_processExecutor != null) {
230 return _processExecutor;
231 }
232
233 synchronized (ProcessExecutor.class) {
234 if (_processExecutor == null) {
235 _processExecutor = new ProcessExecutor();
236
237 _processExecutor.setThreadFactory(
238 new NamedThreadFactory(
239 ImageMagickImpl.class.getName(), Thread.MIN_PRIORITY,
240 ClassLoaderUtil.getPortalClassLoader()));
241 }
242 }
243
244 return _processExecutor;
245 }
246
247 private static final Log _log = LogFactoryUtil.getLog(
248 ImageMagickImpl.class);
249
250 private static final ImageMagickImpl _instance = new ImageMagickImpl();
251
252 private String _globalSearchPath;
253 private volatile ProcessExecutor _processExecutor;
254 private Properties _resourceLimitsProperties;
255 private boolean _warned;
256
257 }