001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.scheduler.jobs;
029
030import org.opencms.file.CmsFile;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsProperty;
033import org.opencms.file.CmsPropertyDefinition;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.file.types.CmsResourceTypeImage;
037import org.opencms.loader.CmsImageLoader;
038import org.opencms.loader.CmsImageScaler;
039import org.opencms.lock.CmsLock;
040import org.opencms.main.CmsException;
041import org.opencms.main.OpenCms;
042import org.opencms.report.CmsLogReport;
043import org.opencms.report.I_CmsReport;
044import org.opencms.scheduler.I_CmsScheduledJob;
045
046import java.util.Collections;
047import java.util.List;
048import java.util.Map;
049
050/**
051 * A schedulable OpenCms job to calculate image size information.<p>
052 *
053 * Image size information is stored in the <code>{@link CmsPropertyDefinition#PROPERTY_IMAGE_SIZE}</code> property
054 * of an image file must have the format "h:x,w:y" with x and y being positive Integer vaulues.<p>
055 *
056 * Job parameters:<p>
057 * <dl>
058 * <dt><code>downscale=true|false</code></dt>
059 * <dd>Controls if images are automatically downscaled according to the configured image
060 * downscale settings, by default this is <code>false</code>.</dd>
061 * </dl>
062 *
063 * @since 6.0.2
064 */
065public class CmsCreateImageSizeJob implements I_CmsScheduledJob {
066
067    /**
068     * This job parameter controls if images are automatically downscaled according to the configured image
069     * downscale settings, by default this is <code>false</code>.
070     *
071     * Possible values are <code>true</code> or <code>false</code> (default).
072     * If this is set to <code>true</code>, then all images are checked against the
073     * configured image downscale settings (see {@link CmsImageLoader#CONFIGURATION_DOWNSCALE}).
074     * If the image is too large, it is automatically downscaled.<p>
075     */
076    public static final String PARAM_DOWNSCALE = "downscale";
077
078    /**
079     * @see org.opencms.scheduler.I_CmsScheduledJob#launch(CmsObject, Map)
080     */
081    public String launch(CmsObject cms, Map<String, String> parameters) throws Exception {
082
083        if (!CmsImageLoader.isEnabled()) {
084            // scaling functions are not available
085            return Messages.get().getBundle().key(Messages.LOG_IMAGE_SCALING_DISABLED_0);
086        }
087
088        // read the downscale parameter
089        boolean downscale = Boolean.valueOf(parameters.get(PARAM_DOWNSCALE)).booleanValue();
090
091        I_CmsReport report = new CmsLogReport(cms.getRequestContext().getLocale(), CmsCreateImageSizeJob.class);
092        report.println(Messages.get().container(Messages.RPT_IMAGE_SIZE_START_0), I_CmsReport.FORMAT_HEADLINE);
093
094        List<CmsResource> resources = Collections.emptyList();
095        try {
096            // get all image resources
097            resources = cms.readResources(
098                "/",
099                CmsResourceFilter.IGNORE_EXPIRATION.addRequireType(
100                    OpenCms.getResourceManager().getResourceType(
101                        CmsResourceTypeImage.getStaticTypeName()).getTypeId()));
102        } catch (CmsException e) {
103            report.println(e);
104        }
105
106        int count = 0;
107        // now iterate through all resources
108        for (int i = 0; i < resources.size(); i++) {
109
110            try {
111
112                CmsResource res = resources.get(i);
113                report.print(
114                    Messages.get().container(
115                        Messages.RPT_IMAGE_SIZE_PROCESS_3,
116                        String.valueOf(i + 1),
117                        String.valueOf(resources.size()),
118                        res.getRootPath()),
119                    I_CmsReport.FORMAT_HEADLINE);
120
121                report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
122
123                // check if the resource is locked by another user
124                // we cannot process resources that are locked by someone else
125                CmsLock lock = cms.getLock(res);
126                if (lock.isNullLock() || lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) {
127
128                    // read the file content
129                    CmsFile file = cms.readFile(res);
130                    // get the image size information
131                    CmsImageScaler scaler = new CmsImageScaler(file.getContents(), file.getRootPath());
132
133                    if (scaler.isValid()) {
134                        // the image can be scaled, width and height are known
135                        boolean updated = false;
136
137                        // check if the image must be downscaled
138                        CmsImageScaler downScaler = null;
139                        if (downscale) {
140                            // scheduled job parameter is set for downscaling
141                            downScaler = CmsResourceTypeImage.getDownScaler(cms, res.getRootPath());
142                        }
143
144                        if (scaler.isDownScaleRequired(downScaler)) {
145                            // downscaling is required - just write the file again, in this case everything is updated
146                            lockResource(cms, lock, res);
147                            cms.writeFile(file);
148                            // calculate the downscaled image size (only used for the output report)
149                            scaler = scaler.getDownScaler(downScaler);
150                            // the resource was updated
151                            updated = true;
152                        } else {
153                            // check if the "image.size" property must be updated
154                            CmsProperty prop = cms.readPropertyObject(
155                                res,
156                                CmsPropertyDefinition.PROPERTY_IMAGE_SIZE,
157                                false);
158                            // update the property if it does not exist or it is different than the newly calculated one
159                            if (prop.isNullProperty() || !prop.getValue().equals(scaler.toString())) {
160                                // lock resource
161                                lockResource(cms, lock, res);
162                                // set the shared value of the property or create a new one if required
163                                if (prop.isNullProperty()) {
164                                    prop = new CmsProperty(
165                                        CmsPropertyDefinition.PROPERTY_IMAGE_SIZE,
166                                        null,
167                                        scaler.toString());
168                                } else {
169                                    // delete any individual proprety value (just in case)
170                                    prop.setStructureValue(CmsProperty.DELETE_VALUE);
171                                    // set the calculated value as shared property
172                                    prop.setResourceValue(scaler.toString());
173                                }
174                                // write the property
175                                cms.writePropertyObject(res.getRootPath(), prop);
176                                // the resource was updated
177                                updated = true;
178                            }
179                        }
180
181                        if (updated) {
182                            // the resource was updated
183                            unlockResource(cms, lock, res);
184                            // increase counter
185                            count++;
186                            // write report information
187                            report.println(
188                                Messages.get().container(Messages.RPT_IMAGE_SIZE_UPDATE_1, scaler.toString()),
189                                I_CmsReport.FORMAT_DEFAULT);
190
191                        } else {
192                            // no changes have been made to the resource
193                            report.println(
194                                Messages.get().container(Messages.RPT_IMAGE_SIZE_SKIP_1, scaler.toString()),
195                                I_CmsReport.FORMAT_DEFAULT);
196                        }
197                    } else {
198                        // no valid image scaler
199                        report.println(
200                            Messages.get().container(Messages.RPT_IMAGE_SIZE_UNABLE_TO_CALCULATE_0),
201                            I_CmsReport.FORMAT_DEFAULT);
202                    }
203                } else {
204                    // the resource is locked by someone else
205                    report.println(
206                        Messages.get().container(Messages.RPT_IMAGE_SIZE_LOCKED_0),
207                        I_CmsReport.FORMAT_DEFAULT);
208                }
209            } catch (CmsException e) {
210                report.println(e);
211            }
212        }
213
214        report.println(Messages.get().container(Messages.RPT_IMAGE_SIZE_END_0), I_CmsReport.FORMAT_HEADLINE);
215
216        return Messages.get().getBundle().key(Messages.LOG_IMAGE_SIZE_UPDATE_COUNT_1, new Integer(count));
217    }
218
219    /**
220     * Locks the given resource (if required).<p>
221     *
222     * @param cms the OpenCms user context
223     * @param lock the previous lock status of the resource
224     * @param res the resource to lock
225     *
226     * @throws CmsException in case something goes wrong
227     */
228    private void lockResource(CmsObject cms, CmsLock lock, CmsResource res) throws CmsException {
229
230        if (lock.isNullLock()) {
231            cms.lockResource(res.getRootPath());
232        }
233    }
234
235    /**
236     * Unlocks the given resource (if required).<p>
237     *
238     * @param cms the OpenCms user context
239     * @param lock the lock of the resource
240     * @param res the resource to lock
241     *
242     * @throws CmsException in case something goes wrong
243     */
244    private void unlockResource(CmsObject cms, CmsLock lock, CmsResource res) throws CmsException {
245
246        if (lock.isNullLock()) {
247            cms.unlockResource(res.getRootPath());
248        }
249    }
250}