001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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.search.documents; 029 030import org.opencms.configuration.CmsParameterConfiguration; 031import org.opencms.file.CmsFile; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsResource; 034import org.opencms.main.CmsException; 035import org.opencms.main.CmsLog; 036import org.opencms.search.I_CmsSearchDocument; 037import org.opencms.search.I_CmsSearchIndex; 038import org.opencms.search.extractors.I_CmsExtractionResult; 039 040import java.util.ArrayList; 041import java.util.Iterator; 042import java.util.List; 043 044import org.apache.commons.logging.Log; 045 046/** 047 * Base document factory class for a VFS <code>{@link org.opencms.file.CmsResource}</code>, 048 * just requires a specialized implementation of 049 * <code>{@link I_CmsDocumentFactory#extractContent(CmsObject, CmsResource, I_CmsSearchIndex)}</code> 050 * for text extraction from the binary document content.<p> 051 * 052 * @since 6.0.0 053 */ 054public abstract class A_CmsVfsDocument implements I_CmsDocumentFactory { 055 056 /** 057 * Generic type name used as default for all types that are globally unconfigured. 058 * Note that any special xml content is already configured if xmlcontent is configured. 059 */ 060 public static final String DEFAULT_ALL_UNCONFIGURED_TYPES = "__unconfigured__"; 061 /** Generic type name used as default for all types. */ 062 public static final String DEFAULT_ALL_TYPES = "__all__"; 063 /** The log object for this class. */ 064 private static final Log LOG = CmsLog.getLog(A_CmsVfsDocument.class); 065 066 /** Name of the document type. */ 067 protected String m_name; 068 069 /** The cache used for storing extracted documents. */ 070 private CmsExtractionResultCache m_cache; 071 072 /** 073 * Creates a new instance of this lucene document factory.<p> 074 * 075 * @param name name of the documenttype 076 */ 077 public A_CmsVfsDocument(String name) { 078 079 m_name = name; 080 } 081 082 /** 083 * Creates a document factory lookup key for the given resource type name / MIME type configuration.<p> 084 * 085 * If the given <code>mimeType</code> is <code>null</code>, this indicates that the key should 086 * match all VFS resource of the given resource type regardless of the MIME type.<p> 087 * 088 * @param type the resource type name to use 089 * @param mimeType the MIME type to use 090 * 091 * @return a document factory lookup key for the given resource id / MIME type configuration 092 */ 093 public static String getDocumentKey(String type, String mimeType) { 094 095 StringBuffer result = new StringBuffer(16); 096 result.append(I_CmsSearchDocument.VFS_DOCUMENT_KEY_PREFIX); 097 result.append('_'); 098 result.append(type); 099 if (mimeType != null) { 100 result.append(':'); 101 result.append(mimeType); 102 } 103 return result.toString(); 104 } 105 106 /** 107 * @see org.opencms.search.I_CmsSearchIndex#addConfigurationParameter(java.lang.String, java.lang.String) 108 */ 109 public void addConfigurationParameter(String key, String value) { 110 111 // by default no parameters are excepted 112 113 } 114 115 /** 116 * {@inheritDoc} 117 * 118 * @see org.opencms.search.documents.I_CmsDocumentFactory#createDocument(CmsObject, CmsResource, I_CmsSearchIndex) 119 */ 120 public I_CmsSearchDocument createDocument(CmsObject cms, CmsResource resource, I_CmsSearchIndex index) 121 throws CmsException { 122 123 // extract the content from the resource 124 I_CmsExtractionResult content = null; 125 126 if (index.isExtractingContent()) { 127 // do full text content extraction only if required 128 129 // check if caching is enabled for this document type 130 CmsExtractionResultCache cache = getCache(); 131 String cacheName = null; 132 if ((cache != null) && (resource.getSiblingCount() > 1)) { 133 // hard drive based caching only makes sense for resources that have siblings, 134 // because the index will also store the content as a blob 135 cacheName = cache.getCacheName( 136 resource, 137 isLocaleDependend() ? index.getLocaleForResource(cms, resource, null) : null, 138 getName()); 139 content = cache.getCacheObject(cacheName); 140 if ((content != null) && LOG.isDebugEnabled()) { 141 LOG.debug("Not re-extracting. Using cached content for '" + resource.getRootPath() + "'."); 142 } 143 } 144 145 if ((content == null) && isOnlyDependentOnContent()) { 146 // extraction result has not been found in the cache 147 // use the currently indexed content, if it is still up to date. 148 content = index.getContentIfUnchanged(resource); 149 if ((content != null) && LOG.isDebugEnabled()) { 150 LOG.debug( 151 "Not re-extracting. Using previously indexed content for '" + resource.getRootPath() + "'."); 152 } 153 } 154 155 if (content == null) { 156 // extraction result has not been attached to the resource 157 try { 158 content = extractContent(cms, resource, index); 159 if (LOG.isDebugEnabled()) { 160 LOG.debug("Extracting content for '" + resource.getRootPath() + "' successful."); 161 } 162 if ((cache != null) && (resource.getSiblingCount() > 1)) { 163 // save extracted content to the cache 164 cache.saveCacheObject(cacheName, content); 165 } 166 } catch (CmsIndexNoContentException e) { 167 // there was no content found for the resource 168 LOG.info( 169 Messages.get().getBundle().key(Messages.ERR_TEXT_EXTRACTION_1, resource.getRootPath()) 170 + " " 171 + e.getMessage()); 172 } catch (Throwable e) { 173 // text extraction failed for document - continue indexing meta information only 174 LOG.error( 175 Messages.get().getBundle().key(Messages.ERR_TEXT_EXTRACTION_1, resource.getRootPath()), 176 e); 177 } 178 } 179 } 180 181 // create the Lucene document according to the index field configuration 182 return index.getFieldConfiguration().createDocument(cms, resource, index, content); 183 } 184 185 /** 186 * @see org.opencms.search.documents.I_CmsDocumentFactory#getCache() 187 */ 188 public CmsExtractionResultCache getCache() { 189 190 return m_cache; 191 } 192 193 /** 194 * Returns the empty configuration. 195 * Override the method if your document factory is configurable. 196 * 197 * @see org.opencms.search.I_CmsSearchIndex#getConfiguration() 198 */ 199 public CmsParameterConfiguration getConfiguration() { 200 201 return new CmsParameterConfiguration(); 202 } 203 204 /** 205 * @see org.opencms.search.documents.I_CmsDocumentFactory#getDocumentKeys(java.util.List, java.util.List) 206 */ 207 public List<String> getDocumentKeys(List<String> resourceTypes, List<String> mimeTypes) throws CmsException { 208 209 List<String> keys = new ArrayList<String>(); 210 211 try { 212 for (Iterator<String> i = resourceTypes.iterator(); i.hasNext();) { 213 214 String typeName = i.next(); 215 if (typeName.equals("*")) { 216 typeName = DEFAULT_ALL_UNCONFIGURED_TYPES; 217 } 218 if (typeName.equals("**")) { 219 typeName = DEFAULT_ALL_TYPES; 220 } 221 for (Iterator<String> j = mimeTypes.iterator(); j.hasNext();) { 222 keys.add(getDocumentKey(typeName, j.next())); 223 } 224 if (mimeTypes.isEmpty()) { 225 keys.add(getDocumentKey(typeName, null)); 226 } 227 } 228 } catch (Exception exc) { 229 throw new CmsException(Messages.get().container(Messages.ERR_CREATE_DOC_KEY_0), exc); 230 } 231 232 return keys; 233 } 234 235 /** 236 * @see org.opencms.search.documents.I_CmsDocumentFactory#getName() 237 */ 238 public String getName() { 239 240 return m_name; 241 } 242 243 /** 244 * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration() 245 */ 246 public void initConfiguration() { 247 248 // Do nothing by default 249 250 } 251 252 /** 253 * @see org.opencms.search.documents.I_CmsDocumentFactory#setCache(org.opencms.search.documents.CmsExtractionResultCache) 254 */ 255 public void setCache(CmsExtractionResultCache cache) { 256 257 m_cache = cache; 258 } 259 260 /** 261 * Logs content extraction for the specified resource and index.<p> 262 * 263 * @param resource the resource to log content extraction for 264 * @param index the search index to log content extraction for 265 */ 266 protected void logContentExtraction(CmsResource resource, I_CmsSearchIndex index) { 267 268 if (LOG.isDebugEnabled()) { 269 LOG.debug( 270 Messages.get().getBundle().key( 271 Messages.LOG_EXTRACT_CONTENT_2, 272 resource.getRootPath(), 273 index.getName())); 274 } 275 } 276 277 /** 278 * Upgrades the given resource to a {@link CmsFile} with content.<p> 279 * 280 * @param cms the current users OpenCms context 281 * @param resource the resource to upgrade 282 * 283 * @return the given resource upgraded to a {@link CmsFile} with content 284 * 285 * @throws CmsException if the resource could not be read 286 * @throws CmsIndexNoContentException if the resource has no content 287 */ 288 protected CmsFile readFile(CmsObject cms, CmsResource resource) throws CmsException, CmsIndexNoContentException { 289 290 CmsFile file = cms.readFile(resource); 291 if (file.getLength() <= 0) { 292 throw new CmsIndexNoContentException( 293 Messages.get().container(Messages.ERR_NO_CONTENT_1, resource.getRootPath())); 294 } 295 return file; 296 } 297}