001/*
002 * File   : $Source$
003 * Date   : $Date$
004 * Version: $Revision$
005 *
006 * This library is part of OpenCms -
007 * the Open Source Content Management System
008 *
009 * Copyright (C) 2002 - 2011 Alkacon Software (http://www.alkacon.com)
010 *
011 * This library is free software; you can redistribute it and/or
012 * modify it under the terms of the GNU Lesser General Public
013 * License as published by the Free Software Foundation; either
014 * version 2.1 of the License, or (at your option) any later version.
015 *
016 * This library is distributed in the hope that it will be useful,
017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019 * Lesser General Public License for more details.
020 *
021 * For further information about Alkacon Software, please see the
022 * company website: http://www.alkacon.com
023 *
024 * For further information about OpenCms, please see the
025 * project website: http://www.opencms.org
026 *
027 * You should have received a copy of the GNU Lesser General Public
028 * License along with this library; if not, write to the Free Software
029 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
030 */
031
032package org.opencms.ade.configuration;
033
034import org.opencms.db.CmsDriverManager;
035import org.opencms.db.CmsPublishedResource;
036import org.opencms.file.CmsObject;
037import org.opencms.file.CmsResource;
038import org.opencms.main.CmsEvent;
039import org.opencms.main.CmsException;
040import org.opencms.main.CmsLog;
041import org.opencms.main.I_CmsEventListener;
042import org.opencms.util.CmsCollectionsGenericWrapper;
043import org.opencms.util.CmsUUID;
044
045import java.util.ArrayList;
046import java.util.List;
047
048import org.apache.commons.logging.Log;
049
050/**
051 *
052 * This event handler manages cache instances which are instances of the interface {@link I_CmsGlobalConfigurationCache}.
053 * It keeps a list of cache instance pairs, each containing one cache for the online mode and one for the offline mode,
054 * and handles events caused by changed resources by notifying the cache instances.
055 *
056 * Note that *all* changed resources will get passed to the underlying cache instances, so those instances will need to check
057 * whether the resource passed into the update or remove methods is actually a resource with which the cache instance is concerned.<p>
058 *
059 * This class should be used if you have an indefinite number of configuration files at arbitrary locations in the VFS.
060 * If you need to cache e.g. a single configuration file with a known, fixed path, using {@link org.opencms.cache.CmsVfsMemoryObjectCache} is
061 * easier.<p>
062 */
063public class CmsGlobalConfigurationCacheEventHandler implements I_CmsEventListener {
064
065    /**
066     * A pair of cache instances, one for the offline mode and one for the online mode.<p>
067     */
068    private class CachePair {
069
070        /** A name for debugging. */
071        @SuppressWarnings("unused")
072        private String m_debugName;
073
074        /** The offline cache instance. */
075        private I_CmsGlobalConfigurationCache m_offlineCache;
076
077        /** The online cache instance. */
078        private I_CmsGlobalConfigurationCache m_onlineCache;
079
080        /**
081         * Creates a new cache pair.<p>
082         *
083         * @param offlineCache the offline cache instance
084         * @param onlineCache the online cache instance
085         * @param debugName the name for debugging
086         */
087        public CachePair(
088            I_CmsGlobalConfigurationCache offlineCache,
089            I_CmsGlobalConfigurationCache onlineCache,
090            String debugName) {
091
092            m_offlineCache = offlineCache;
093            m_onlineCache = onlineCache;
094            m_debugName = debugName;
095        }
096
097        /**
098         * Gets the offline cache instance.<p>
099         *
100         * @return the offline cache instance
101         */
102        public I_CmsGlobalConfigurationCache getOfflineCache() {
103
104            return m_offlineCache;
105        }
106
107        /**
108         * Gets the online cache instance.<p>
109         *
110         * @return the online cache instance
111         */
112        public I_CmsGlobalConfigurationCache getOnlineCache() {
113
114            return m_onlineCache;
115        }
116    }
117
118    /** The logger instance for this class. */
119    private static final Log LOG = CmsLog.getLog(CmsGlobalConfigurationCacheEventHandler.class);
120
121    /** The list of cache pairs. */
122    private List<CachePair> m_caches = new ArrayList<CachePair>();
123
124    /** An online CMS object. */
125    private CmsObject m_onlineCms;
126
127    /** Creates a new cache event handler.
128     *
129     * @param onlineCms an online CMS object
130     **/
131    public CmsGlobalConfigurationCacheEventHandler(CmsObject onlineCms) {
132
133        m_onlineCms = onlineCms;
134    }
135
136    /**
137     * Adds a new pair of cache instances which should be managed by this event handler.<p>
138     *
139     * @param offlineCache the offline cache instance
140     * @param onlineCache the online cache instance
141     * @param debugName an identifier used for debugging
142     */
143    public void addCache(
144        I_CmsGlobalConfigurationCache offlineCache,
145        I_CmsGlobalConfigurationCache onlineCache,
146        String debugName) {
147
148        CachePair cachePair = new CachePair(offlineCache, onlineCache, debugName);
149        m_caches.add(cachePair);
150    }
151
152    /**
153     * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent)
154     */
155    public void cmsEvent(CmsEvent event) {
156
157        CmsResource resource = null;
158        List<CmsResource> resources = null;
159        List<Object> irrelevantChangeTypes = new ArrayList<Object>();
160        irrelevantChangeTypes.add(Integer.valueOf(CmsDriverManager.NOTHING_CHANGED));
161        irrelevantChangeTypes.add(Integer.valueOf(CmsDriverManager.CHANGED_PROJECT));
162        //System.out.println();
163        switch (event.getType()) {
164            case I_CmsEventListener.EVENT_RESOURCE_AND_PROPERTIES_MODIFIED:
165            case I_CmsEventListener.EVENT_RESOURCE_MODIFIED:
166            case I_CmsEventListener.EVENT_RESOURCE_CREATED:
167                //System.out.print(getEventName(event.getType()));
168                Object change = event.getData().get(I_CmsEventListener.KEY_CHANGE);
169                if ((change != null) && irrelevantChangeTypes.contains(change)) {
170                    // skip lock & unlock, and project changes
171                    return;
172                }
173                resource = (CmsResource)event.getData().get(I_CmsEventListener.KEY_RESOURCE);
174                offlineCacheUpdate(resource);
175                //System.out.print(" " + resource.getRootPath());
176                break;
177            case I_CmsEventListener.EVENT_RESOURCES_AND_PROPERTIES_MODIFIED:
178                // a list of resources and all of their properties have been modified
179                //System.out.print(getEventName(event.getType()));
180                resources = CmsCollectionsGenericWrapper.list(event.getData().get(I_CmsEventListener.KEY_RESOURCES));
181                for (CmsResource res : resources) {
182                    offlineCacheUpdate(res);
183                    //System.out.print(" " + res.getRootPath());
184                }
185                break;
186
187            case I_CmsEventListener.EVENT_RESOURCE_MOVED:
188                resources = CmsCollectionsGenericWrapper.list(event.getData().get(I_CmsEventListener.KEY_RESOURCES));
189                // source, source folder, dest, dest folder
190                // - OR -
191                // source, dest, dest folder
192                offlineCacheRemove(resources.get(0));
193                offlineCacheUpdate(resources.get(resources.size() - 2));
194                break;
195
196            case I_CmsEventListener.EVENT_RESOURCE_DELETED:
197                resources = CmsCollectionsGenericWrapper.list(event.getData().get(I_CmsEventListener.KEY_RESOURCES));
198                for (CmsResource res : resources) {
199                    offlineCacheRemove(res);
200                }
201                break;
202            case I_CmsEventListener.EVENT_RESOURCES_MODIFIED:
203                //System.out.print(getEventName(event.getType()));
204                // a list of resources has been modified
205                resources = CmsCollectionsGenericWrapper.list(event.getData().get(I_CmsEventListener.KEY_RESOURCES));
206                for (CmsResource res : resources) {
207                    offlineCacheUpdate(res);
208                }
209                break;
210            case I_CmsEventListener.EVENT_CLEAR_ONLINE_CACHES:
211                onlineCacheClear();
212                break;
213            case I_CmsEventListener.EVENT_PUBLISH_PROJECT:
214                //System.out.print(getEventName(event.getType()));
215                String publishIdStr = (String)event.getData().get(I_CmsEventListener.KEY_PUBLISHID);
216                if (publishIdStr != null) {
217                    CmsUUID publishId = new CmsUUID(publishIdStr);
218                    try {
219                        List<CmsPublishedResource> publishedResources = m_onlineCms.readPublishedResources(publishId);
220                        if (publishedResources.isEmpty()) {
221                            // normally, the list of published resources should not be empty.
222                            // If it is, the publish event is not coming from a normal publish process,
223                            // so we re-initialize the whole cache to be on the safe side.
224                            onlineCacheClear();
225                        } else {
226                            for (CmsPublishedResource res : publishedResources) {
227                                if (res.getState().isDeleted()) {
228                                    onlineCacheRemove(res);
229                                } else {
230                                    onlineCacheUpdate(res);
231                                }
232                            }
233                        }
234                    } catch (CmsException e) {
235                        LOG.error(e.getLocalizedMessage(), e);
236                    }
237                }
238                break;
239            case I_CmsEventListener.EVENT_CLEAR_CACHES:
240                //System.out.print(getEventName(event.getType()));
241                offlineCacheClear();
242                onlineCacheClear();
243                break;
244            case I_CmsEventListener.EVENT_CLEAR_OFFLINE_CACHES:
245                //System.out.print(getEventName(event.getType()));
246                offlineCacheClear();
247                break;
248            default:
249                // noop
250                break;
251        }
252    }
253
254    /**
255     * Clears the offline caches.<p>
256     */
257    protected void offlineCacheClear() {
258
259        for (CachePair cachePair : m_caches) {
260            try {
261                cachePair.getOfflineCache().clear();
262            } catch (Throwable t) {
263                LOG.error(t.getLocalizedMessage(), t);
264            }
265        }
266    }
267
268    /**
269     * Removes a resource from the offline caches.<p>
270     *
271     * @param resource the resource to remove
272     */
273    protected void offlineCacheRemove(CmsPublishedResource resource) {
274
275        for (CachePair cachePair : m_caches) {
276            try {
277                cachePair.getOfflineCache().remove(resource);
278            } catch (Throwable e) {
279                LOG.error(e.getLocalizedMessage());
280            }
281        }
282    }
283
284    /**
285     * Removes a resource from the offline caches.<p>
286     *
287     * @param resource the resource to remove
288     */
289    protected void offlineCacheRemove(CmsResource resource) {
290
291        for (CachePair cachePair : m_caches) {
292            try {
293                cachePair.getOfflineCache().remove(resource);
294            } catch (Throwable e) {
295                LOG.error(e.getLocalizedMessage());
296            }
297        }
298    }
299
300    /**
301     * Updates a resource in the offline caches.<p>
302     *
303     * @param resource the resource to update
304     */
305    protected void offlineCacheUpdate(CmsPublishedResource resource) {
306
307        for (CachePair cachePair : m_caches) {
308            try {
309                cachePair.getOfflineCache().update(resource);
310            } catch (Throwable e) {
311                LOG.error(e.getLocalizedMessage());
312            }
313        }
314
315    }
316
317    /**
318     * Updates a resource in the offline caches.<p>
319     *
320     * @param resource the resource to update
321     */
322    protected void offlineCacheUpdate(CmsResource resource) {
323
324        for (CachePair cachePair : m_caches) {
325            try {
326                cachePair.getOfflineCache().update(resource);
327            } catch (Throwable e) {
328                LOG.error(e.getLocalizedMessage());
329            }
330        }
331
332    }
333
334    /**
335     * Clears the online caches.<p>
336     */
337    protected void onlineCacheClear() {
338
339        for (CachePair cachePair : m_caches) {
340            try {
341                cachePair.getOnlineCache().clear();
342            } catch (Throwable e) {
343                LOG.error(e.getLocalizedMessage(), e);
344            }
345        }
346    }
347
348    /**
349     * Removes a resource from the online caches.<p>
350     *
351     * @param resource the resource to remove
352     */
353    protected void onlineCacheRemove(CmsPublishedResource resource) {
354
355        for (CachePair cachePair : m_caches) {
356            try {
357                cachePair.getOnlineCache().remove(resource);
358            } catch (Throwable e) {
359                LOG.error(e.getLocalizedMessage());
360            }
361        }
362    }
363
364    /**
365     * Removes a resource from the online caches.<p>
366     *
367     * @param resource the resource to remove
368     */
369    protected void onlineCacheRemove(CmsResource resource) {
370
371        for (CachePair cachePair : m_caches) {
372            try {
373                cachePair.getOnlineCache().remove(resource);
374            } catch (Throwable e) {
375                LOG.error(e.getLocalizedMessage());
376            }
377        }
378
379    }
380
381    /**
382     * Updates a resource in the online caches.<p>
383     *
384     * @param resource the resource to update
385     */
386    protected void onlineCacheUpdate(CmsPublishedResource resource) {
387
388        for (CachePair cachePair : m_caches) {
389            try {
390                cachePair.getOnlineCache().update(resource);
391            } catch (Throwable e) {
392                LOG.error(e.getLocalizedMessage());
393            }
394        }
395
396    }
397
398    /**
399     * Updates a resource in the online caches.<p>
400     *
401     * @param resource the resource to update
402     */
403    protected void onlineCacheUpdate(CmsResource resource) {
404
405        for (CachePair cachePair : m_caches) {
406            try {
407                cachePair.getOnlineCache().update(resource);
408            } catch (Throwable e) {
409                LOG.error(e.getLocalizedMessage());
410            }
411        }
412    }
413}