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.workplace.tools.content.check;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.main.CmsException;
034import org.opencms.main.CmsLog;
035import org.opencms.report.I_CmsReport;
036import org.opencms.util.CmsFileUtil;
037import org.opencms.workplace.CmsWorkplace;
038import org.opencms.workplace.tools.CmsToolManager;
039
040import java.util.ArrayList;
041import java.util.Iterator;
042import java.util.List;
043import java.util.SortedMap;
044import java.util.TreeMap;
045
046import org.apache.commons.logging.Log;
047
048/**
049 * Main implementation of the content check. It maintains all configured content check
050 * plugins and handles the check process.<p>
051 *
052 *
053 * @since 6.1.2
054 */
055public class CmsContentCheck {
056
057    /**  Path to the plugin folder. */
058    public static final String VFS_PATH_PLUGIN_FOLDER = CmsWorkplace.VFS_PATH_WORKPLACE
059        + "admin/contenttools/check/plugin/";
060
061    /** The log object for this class. */
062    private static final Log LOG = CmsLog.getLog(CmsContentCheck.class);
063
064    /** The CmsObject. */
065    private CmsObject m_cms;
066
067    /** The list of paths to be processed. */
068    private List m_paths;
069
070    /** The list of all instantiated plugins. */
071    private List m_plugins;
072
073    /** The report for the output. */
074    private I_CmsReport m_report;
075
076    /** The map of resources to check. */
077    private SortedMap m_resources;
078
079    /** The content check result. */
080    private CmsContentCheckResult m_result;
081
082    /**
083     * Constructor, creates a new CmsContentCheck. <p>
084     *
085     * @param cms the CmsObject
086     */
087    public CmsContentCheck(CmsObject cms) {
088
089        m_cms = cms;
090        m_plugins = new ArrayList();
091        m_paths = new ArrayList();
092        m_result = new CmsContentCheckResult();
093
094        try {
095            init();
096        } catch (CmsException e) {
097            // TODO: do some errorhandling
098        }
099    }
100
101    /**
102     * Gets a list of all paths to be processed by the content check plugins.<p>
103     *
104     * @return list of vfs paths
105     */
106    public List getPaths() {
107
108        return m_paths;
109    }
110
111    /**
112     * Gets a list of instances of all available content check plugins.<p>
113     *
114     * @return list of plugin instances.
115     */
116    public List getPlugins() {
117
118        return m_plugins;
119    }
120
121    /**
122     * Gets the number of installed plugins.<p>
123     *
124     * @return number of installed plugins
125     */
126    public int getPluginsCount() {
127
128        return m_plugins.size();
129    }
130
131    /**
132     * Gets the results of the content check.<p>
133     *
134     * @return CmsContentCheckResult object containing all resources that collected an error or warning
135     */
136    public CmsContentCheckResult getResults() {
137
138        return m_result;
139    }
140
141    /**
142     * Sets the list of all paths to be processed by the content check plugins.<p>
143     * @param paths list of vfs paths
144     */
145    public void setPaths(List paths) {
146
147        m_paths = paths;
148    }
149
150    /**
151     * Starts the content check.<p>
152     *
153     * @param cms the CmsObject
154     * @param report StringBuffer for reporting
155     * @throws Exception if something goes wrong
156     */
157    public void startContentCheck(CmsObject cms, I_CmsReport report) throws Exception {
158
159        m_report = report;
160        m_report.println(Messages.get().container(Messages.RPT_CONTENT_CHECK_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
161
162        // collect all resources
163        m_report.print(Messages.get().container(Messages.RPT_CONTENT_COLLECT_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
164        m_report.println(
165            org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
166            I_CmsReport.FORMAT_HEADLINE);
167        m_resources = collectResources(cms);
168        m_report.print(
169            org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
170            I_CmsReport.FORMAT_HEADLINE);
171        m_report.println(
172            Messages.get().container(Messages.RPT_CONTENT_COLLECT_END_1, Integer.valueOf(m_resources.size())),
173            I_CmsReport.FORMAT_HEADLINE);
174
175        // now process all resources
176        m_report.print(Messages.get().container(Messages.RPT_CONTENT_PROCESS_BEGIN_0), I_CmsReport.FORMAT_HEADLINE);
177        m_report.println(
178            org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
179            I_CmsReport.FORMAT_HEADLINE);
180        int count = 1;
181        Iterator i = m_resources.keySet().iterator();
182        while (i.hasNext()) {
183            String resourceName = (String)i.next();
184            CmsContentCheckResource res = (CmsContentCheckResource)m_resources.get(resourceName);
185            boolean errorWarning = false;
186            m_report.print(
187                Messages.get().container(
188                    Messages.RPT_CONTENT_PROCESS_2,
189                    Integer.valueOf(count++),
190                    Integer.valueOf(m_resources.size())),
191                I_CmsReport.FORMAT_NOTE);
192            m_report.print(
193                Messages.get().container(Messages.RPT_CONTENT_PROCESS_RESOURCE_1, res.getResourceName()),
194                I_CmsReport.FORMAT_DEFAULT);
195            m_report.print(
196                org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
197                I_CmsReport.FORMAT_NOTE);
198
199            // loop through all plugins and perform each content check
200            Iterator j = getPlugins().iterator();
201            while (j.hasNext()) {
202                I_CmsContentCheck plugin = (I_CmsContentCheck)j.next();
203                if (plugin.isActive()) {
204                    try {
205                        // process the content check
206                        res = plugin.executeContentCheck(m_cms, res);
207
208                    } catch (CmsException e) {
209                        errorWarning = true;
210                        m_report.println(Messages.get().container(Messages.RPT_EMPTY_0), I_CmsReport.FORMAT_DEFAULT);
211                        m_report.print(
212                            Messages.get().container(Messages.RPT_CONTENT_PROCESS_ERROR_2, plugin.getName(), e),
213                            I_CmsReport.FORMAT_ERROR);
214                    }
215                }
216            }
217
218            // check if there are some errors
219            List errors = res.getErrors();
220            if ((errors != null) && (errors.size() > 0)) {
221                errorWarning = true;
222                m_report.println(Messages.get().container(Messages.RPT_EMPTY_0), I_CmsReport.FORMAT_DEFAULT);
223                m_report.println(
224                    Messages.get().container(Messages.RPT_CONTENT_PROCESS_ERROR_0),
225                    I_CmsReport.FORMAT_ERROR);
226                Iterator k = errors.iterator();
227                while (k.hasNext()) {
228                    String error = (String)k.next();
229                    m_report.println(
230                        Messages.get().container(Messages.RPT_CONTENT_PROCESS_ERROR_1, error),
231                        I_CmsReport.FORMAT_ERROR);
232
233                }
234            }
235
236            // check if there are some warnings
237            List warnings = res.getWarnings();
238            if ((warnings != null) && (warnings.size() > 0)) {
239                errorWarning = true;
240                m_report.println(Messages.get().container(Messages.RPT_EMPTY_0), I_CmsReport.FORMAT_DEFAULT);
241                m_report.println(
242                    Messages.get().container(Messages.RPT_CONTENT_PROCESS_WARNING_0),
243                    I_CmsReport.FORMAT_WARNING);
244                Iterator k = warnings.iterator();
245                while (k.hasNext()) {
246                    String warning = (String)k.next();
247                    m_report.println(
248                        Messages.get().container(Messages.RPT_CONTENT_PROCESS_WARNING_1, warning),
249                        I_CmsReport.FORMAT_WARNING);
250
251                }
252            }
253
254            // store the updated CmsContentCheckResource
255            m_resources.put(resourceName, res);
256
257            if (!errorWarning) {
258                m_report.println(
259                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
260                    I_CmsReport.FORMAT_OK);
261            } else {
262                // there was an error or warning, so store the CmsContentCheckResource
263                // in the results
264                m_result.addResult(res);
265            }
266        }
267        m_report.print(
268            org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
269            I_CmsReport.FORMAT_HEADLINE);
270        m_report.println(Messages.get().container(Messages.RPT_CONTENT_PROCESS_END_0), I_CmsReport.FORMAT_HEADLINE);
271
272        m_report.println(Messages.get().container(Messages.RPT_CONTENT_CHECK_END_0), I_CmsReport.FORMAT_HEADLINE);
273    }
274
275    /**
276     *
277     * @see java.lang.Object#toString()
278     */
279    @Override
280    public String toString() {
281
282        StringBuffer buf = new StringBuffer();
283        buf.append("CmsContentCheck Paths=[");
284        for (int i = 0; i < m_paths.size(); i++) {
285            String path = (String)m_paths.get(i);
286            buf.append(path);
287            if (i < (m_paths.size() - 1)) {
288                buf.append(",");
289            }
290        }
291        buf.append("] Plugins=[");
292        for (int i = 0; i < m_plugins.size(); i++) {
293            I_CmsContentCheck plugin = (I_CmsContentCheck)m_plugins.get(i);
294            buf.append(plugin.getName());
295            buf.append(" (");
296            buf.append(plugin.isActive());
297            buf.append(")");
298            if (i < (m_plugins.size() - 1)) {
299                buf.append(",");
300            }
301        }
302        buf.append("]");
303        return buf.toString();
304    }
305
306    /**
307     * Collects all resources required for the content checks and stores them in a Set.<p>
308     *
309     * The collection of resources is build based on the vfs paths stored in this object.
310     * To prevent that resources are collected multiple times (in case of overlapping vfs paths),
311     * the results will be stored as a map.
312     *
313     * @param cms the CmsObject
314     * @return map of CmsContentCheckResources
315     */
316    private SortedMap collectResources(CmsObject cms) {
317
318        SortedMap collectedResources = new TreeMap();
319
320        // get all vfs paths and extract the resources from there
321        Iterator i = CmsFileUtil.removeRedundancies(m_paths).iterator();
322        while (i.hasNext()) {
323            String path = (String)i.next();
324            m_report.print(
325                Messages.get().container(Messages.RPT_EXTRACT_FROM_PATH_BEGIN_1, path),
326                I_CmsReport.FORMAT_HEADLINE);
327            m_report.println(
328                org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
329                I_CmsReport.FORMAT_HEADLINE);
330            try {
331                List resources = cms.readResources(path, CmsResourceFilter.IGNORE_EXPIRATION, true);
332                // get the single resources and store them
333                Iterator j = resources.iterator();
334                while (j.hasNext()) {
335                    CmsResource res = (CmsResource)j.next();
336                    //                    m_report.print(
337                    //                        Messages.get().container(Messages.RPT_EXTRACT_FROM_PATH_1, cms.getSitePath(res)),
338                    //                        I_CmsReport.FORMAT_DEFAULT);
339                    //                    m_report.print(
340                    //                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
341                    //                        I_CmsReport.FORMAT_DEFAULT);
342                    // create a CmsContentCheckResource for each resource
343                    CmsContentCheckResource contentCheckRes = new CmsContentCheckResource(res);
344                    collectedResources.put(contentCheckRes.getResourceName(), contentCheckRes);
345                    //                    m_report.println(
346                    //                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
347                    //                        I_CmsReport.FORMAT_OK);
348                }
349
350            } catch (CmsException e) {
351                m_report.println(
352                    Messages.get().container(Messages.RPT_EXTRACT_FROM_PATH_ERROR_2, path, e),
353                    I_CmsReport.FORMAT_ERROR);
354            }
355
356            m_report.print(
357                org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0),
358                I_CmsReport.FORMAT_HEADLINE);
359            m_report.println(
360                Messages.get().container(Messages.RPT_EXTRACT_FROM_PATH_END_0),
361                I_CmsReport.FORMAT_HEADLINE);
362
363        }
364
365        return collectedResources;
366    }
367
368    /**
369     * Initializes the CmsContent check and reads all available plugins.<p>
370     *
371     * @throws CmsException if somehting goes wrong
372     */
373    private void init() throws CmsException {
374
375        // first get all installed plugins
376        // plugins are subfolders of the "/plugin/" folder with a template
377        // property holdding the name of the plugin class
378        List resources = m_cms.readResourcesWithProperty(VFS_PATH_PLUGIN_FOLDER, CmsToolManager.HANDLERCLASS_PROPERTY);
379        Iterator i = resources.iterator();
380        while (i.hasNext()) {
381            CmsResource res = (CmsResource)i.next();
382            // ony check folders
383            if (res.isFolder()) {
384                String classname = m_cms.readPropertyObject(
385                    res.getRootPath(),
386                    CmsToolManager.HANDLERCLASS_PROPERTY,
387                    false).getValue();
388                try {
389                    Object objectInstance = Class.forName(classname).newInstance();
390                    if (objectInstance instanceof I_CmsContentCheck) {
391                        I_CmsContentCheck plugin = (I_CmsContentCheck)objectInstance;
392                        plugin.init(m_cms);
393                        // store the plugin instance
394                        m_plugins.add(plugin);
395                        LOG.info(Messages.get().getBundle().key(Messages.LOG_CREATE_PLUGIN_1, classname));
396                    } else {
397                        LOG.warn(Messages.get().getBundle().key(Messages.LOG_CANNOT_CREATE_PLUGIN_1, classname));
398                    }
399                } catch (Throwable t) {
400                    LOG.error(Messages.get().getBundle().key(Messages.LOG_CANNOT_CREATE_PLUGIN_2, classname, t));
401                }
402            }
403        }
404    }
405
406}