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