/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.image; import com.liferay.portal.kernel.configuration.Filter; import com.liferay.portal.kernel.image.ImageMagick; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.security.pacl.DoPrivileged; import com.liferay.portal.kernel.util.ClassLoaderUtil; import com.liferay.portal.kernel.util.NamedThreadFactory; import com.liferay.portal.kernel.util.OSDetector; import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.util.PrefsPropsUtil; import com.liferay.portal.util.PropsUtil; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Future; import javax.portlet.PortletPreferences; import org.im4java.process.ArrayListOutputConsumer; import org.im4java.process.ProcessExecutor; import org.im4java.process.ProcessTask; /** * @author Alexander Chow * @author Ivica Cardic */ @DoPrivileged public class ImageMagickImpl implements ImageMagick { public static ImageMagickImpl getInstance() { return _instance; } @Override public Future<?> convert(List<String> arguments) throws Exception { if (!isEnabled()) { throw new IllegalStateException( "Cannot call \"convert\" when ImageMagick is disabled"); } ProcessExecutor processExecutor = _getProcessExecutor(); LiferayConvertCmd liferayConvertCmd = new LiferayConvertCmd(); ProcessTask processTask = liferayConvertCmd.getProcessTask( _globalSearchPath, getResourceLimits(), arguments); processExecutor.execute(processTask); return processTask; } @Override public void destroy() { if (_processExecutor == null) { return; } synchronized (ProcessExecutor.class) { _processExecutor.shutdownNow(); } _processExecutor = null; } @Override public String getGlobalSearchPath() throws Exception { PortletPreferences preferences = PrefsPropsUtil.getPreferences(true); String globalSearchPath = preferences.getValue( PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, null); if (Validator.isNotNull(globalSearchPath)) { return globalSearchPath; } String filterName = null; if (OSDetector.isApple()) { filterName = "apple"; } else if (OSDetector.isWindows()) { filterName = "windows"; } else { filterName = "unix"; } return PropsUtil.get( PropsKeys.IMAGEMAGICK_GLOBAL_SEARCH_PATH, new Filter(filterName)); } @Override public Properties getResourceLimitsProperties() throws Exception { Properties resourceLimitsProperties = PrefsPropsUtil.getProperties( PropsKeys.IMAGEMAGICK_RESOURCE_LIMIT, true); if (resourceLimitsProperties.isEmpty()) { resourceLimitsProperties = PropsUtil.getProperties( PropsKeys.IMAGEMAGICK_RESOURCE_LIMIT, true); } return resourceLimitsProperties; } @Override public String[] identify(List<String> arguments) throws Exception { if (!isEnabled()) { throw new IllegalStateException( "Cannot call \"identify\" when ImageMagick is disabled"); } ProcessExecutor processExecutor = _getProcessExecutor(); LiferayIdentifyCmd liferayIdentifyCmd = new LiferayIdentifyCmd(); ArrayListOutputConsumer arrayListOutputConsumer = new ArrayListOutputConsumer(); liferayIdentifyCmd.setOutputConsumer(arrayListOutputConsumer); ProcessTask processTask = liferayIdentifyCmd.getProcessTask( _globalSearchPath, getResourceLimits(), arguments); processExecutor.execute(processTask); processTask.get(); List<String> output = arrayListOutputConsumer.getOutput(); if (output != null) { return output.toArray(new String[output.size()]); } return new String[0]; } @Override public boolean isEnabled() { boolean enabled = false; try { enabled = PrefsPropsUtil.getBoolean(PropsKeys.IMAGEMAGICK_ENABLED); } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } if (!enabled && !_warned && _log.isWarnEnabled()) { StringBundler sb = new StringBundler(7); sb.append("Liferay is not configured to use ImageMagick and "); sb.append("Ghostscript. For better quality document and image "); sb.append("previews, install ImageMagick and Ghostscript. Enable "); sb.append("ImageMagick in portal-ext.properties or in the Server "); sb.append("Administration section of the Control Panel at: "); sb.append("http://<server>/group/control_panel/manage/-/server/"); sb.append("external-services"); _log.warn(sb.toString()); _warned = true; } return enabled; } @Override public void reset() { if (isEnabled()) { try { _globalSearchPath = getGlobalSearchPath(); _resourceLimitsProperties = getResourceLimitsProperties(); } catch (Exception e) { _log.error(e, e); } } } protected LinkedList<String> getResourceLimits() { LinkedList<String> resourceLimits = new LinkedList<>(); if (_resourceLimitsProperties == null) { return resourceLimits; } for (Map.Entry<Object, Object> entry : _resourceLimitsProperties.entrySet()) { String value = (String)entry.getValue(); if (Validator.isNull(value)) { continue; } resourceLimits.add("-limit"); resourceLimits.add((String)entry.getKey()); resourceLimits.add(value); } return resourceLimits; } private ProcessExecutor _getProcessExecutor() { if (_processExecutor != null) { return _processExecutor; } synchronized (ProcessExecutor.class) { if (_processExecutor == null) { _processExecutor = new ProcessExecutor(); _processExecutor.setThreadFactory( new NamedThreadFactory( ImageMagickImpl.class.getName(), Thread.MIN_PRIORITY, ClassLoaderUtil.getPortalClassLoader())); } } return _processExecutor; } private static final Log _log = LogFactoryUtil.getLog( ImageMagickImpl.class); private static final ImageMagickImpl _instance = new ImageMagickImpl(); private String _globalSearchPath; private volatile ProcessExecutor _processExecutor; private Properties _resourceLimitsProperties; private boolean _warned; }