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 - 2009 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.search;
033
034import org.opencms.db.CmsPublishedResource;
035import org.opencms.file.CmsProject;
036import org.opencms.file.CmsResource;
037import org.opencms.file.CmsResourceFilter;
038import org.opencms.main.CmsException;
039import org.opencms.main.CmsLog;
040import org.opencms.report.I_CmsReport;
041import org.opencms.search.documents.CmsDocumentDependency;
042
043import java.util.ArrayList;
044import java.util.HashMap;
045import java.util.Iterator;
046import java.util.List;
047import java.util.Map;
048
049import org.apache.commons.logging.Log;
050
051/**
052 * A VFS indexer that resolves locale dependent documents.<p>
053 *
054 * @since 8.5.0
055 */
056public class CmsDependencyIndexer extends CmsVfsIndexer {
057
058    /** The log object for this class. */
059    private static final Log LOG = CmsLog.getLog(CmsDependencyIndexer.class);
060
061    /**
062     * @see org.opencms.search.CmsVfsIndexer#getUpdateData(org.opencms.search.CmsSearchIndexSource, java.util.List)
063     */
064    @Override
065    public CmsSearchIndexUpdateData getUpdateData(
066        CmsSearchIndexSource source,
067        List<CmsPublishedResource> publishedResources) {
068
069        try {
070            // create a new update collection from this indexer and the given index source
071            CmsSearchIndexUpdateData result = new CmsSearchIndexUpdateData(source, this);
072            Iterator<CmsPublishedResource> i = publishedResources.iterator();
073            while (i.hasNext()) {
074                // check all published resources if they match this indexer / source
075                CmsPublishedResource pubRes = i.next();
076                // VFS resources will always have a structure id
077                if (!pubRes.getStructureId().isNullUUID() && pubRes.isFile()) {
078                    // use utility method from CmsProject to check if published resource is "inside" this index source
079                    if (CmsProject.isInsideProject(source.getResourcesNames(), pubRes.getRootPath())) {
080                        // the resource is "inside" this index source
081                        CmsPublishedResource pub = pubRes;
082                        // grab the dependencies of this resource
083                        CmsDocumentDependency dep = CmsDocumentDependency.load(m_cms, pub);
084                        List<CmsDocumentDependency> depsAvailable = dep.getDependencies();
085                        Iterator<CmsDocumentDependency> depIt = depsAvailable.iterator();
086                        do {
087                            // add the document to the search index update data
088                            addResourceToUpdateData(pub, result);
089                            if (!pub.getState().isDeleted()) {
090                                // don't need to store dependency info for deleted resources
091                                dep.storeInContext(m_cms);
092                            }
093                            if (depIt.hasNext()) {
094                                // add all dependent documents that are not already included in the original publish list
095                                pub = depIt.next().getResource();
096                            } else {
097                                dep = null;
098                            }
099                        } while (dep != null);
100                    }
101                }
102            }
103            return result;
104        } catch (Exception e) {
105            LOG.error(e.getLocalizedMessage(), e);
106        }
107        return null;
108    }
109
110    /**
111     * @see org.opencms.search.CmsVfsIndexer#isLocaleDependenciesEnable()
112     */
113    @Override
114    public boolean isLocaleDependenciesEnable() {
115
116        return true;
117    }
118
119    /**
120     * @see org.opencms.search.I_CmsIndexer#rebuildIndex(org.opencms.search.I_CmsIndexWriter, org.opencms.search.CmsIndexingThreadManager, org.opencms.search.CmsSearchIndexSource)
121     */
122    @Override
123    public void rebuildIndex(
124        I_CmsIndexWriter writer,
125        CmsIndexingThreadManager threadManager,
126        CmsSearchIndexSource source) {
127
128        List<String> resourceNames = source.getResourcesNames();
129        Iterator<String> i = resourceNames.iterator();
130        while (i.hasNext()) {
131            // read the resources from all configured source folders
132            String resourceName = i.next();
133            List<CmsResource> resources = null;
134            try {
135                // read all resources (only files) below the given path
136                resources = m_cms.readResources(resourceName, CmsResourceFilter.IGNORE_EXPIRATION.addRequireFile());
137            } catch (CmsException e) {
138                if (m_report != null) {
139                    m_report.println(
140                        Messages.get().container(
141                            Messages.RPT_UNABLE_TO_READ_SOURCE_2,
142                            resourceName,
143                            e.getLocalizedMessage()),
144                        I_CmsReport.FORMAT_WARNING);
145                }
146                if (LOG.isWarnEnabled()) {
147                    LOG.warn(
148                        Messages.get().getBundle().key(
149                            Messages.LOG_UNABLE_TO_READ_SOURCE_2,
150                            resourceName,
151                            m_index.getName()),
152                        e);
153                }
154            }
155            if (resources != null) {
156                Map<String, List<CmsResource>> folderLookupMap = createFolderLookupMap(resources);
157                // iterate all resources found in the folder
158                for (CmsResource resource : resources) {
159                    List<CmsResource> folderContent = folderLookupMap.get(
160                        CmsResource.getFolderPath(resource.getRootPath()));
161                    CmsDocumentDependency dep = CmsDocumentDependency.load(m_cms, resource, folderContent);
162                    dep.storeInContext(m_cms);
163                    // now update all the resources individually
164                    updateResource(writer, threadManager, resource);
165                }
166            }
167        }
168    }
169
170    /**
171     * Creates a folder based lookup map for the given resource list.<p>
172     *
173     * @param resources the list of resource to build the lookup map for
174     *
175     * @return a folder based lookup map for the given resource list
176     */
177    protected Map<String, List<CmsResource>> createFolderLookupMap(List<CmsResource> resources) {
178
179        Map<String, List<CmsResource>> result = new HashMap<String, List<CmsResource>>(128);
180        Iterator<CmsResource> i = resources.iterator();
181        while (i.hasNext()) {
182            CmsResource res = i.next();
183            String folderPath = CmsResource.getFolderPath(res.getRootPath());
184            List<CmsResource> folderContent = result.get(folderPath);
185            if (folderContent == null) {
186                folderContent = new ArrayList<CmsResource>(32);
187                result.put(folderPath, folderContent);
188            }
189            folderContent.add(res);
190        }
191
192        return result;
193    }
194}