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