001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.cocoon.components.search.components.impl;
017:
018: import java.io.File;
019: import java.io.IOException;
020: import java.text.SimpleDateFormat;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import org.apache.avalon.framework.configuration.Configurable;
025: import org.apache.avalon.framework.configuration.Configuration;
026: import org.apache.avalon.framework.configuration.ConfigurationException;
027: import org.apache.avalon.framework.logger.AbstractLogEnabled;
028: import org.apache.avalon.framework.service.ServiceException;
029: import org.apache.avalon.framework.service.ServiceManager;
030: import org.apache.avalon.framework.service.Serviceable;
031: import org.apache.avalon.framework.thread.ThreadSafe;
032: import org.apache.cocoon.components.search.Index;
033: import org.apache.cocoon.components.search.IndexException;
034: import org.apache.cocoon.components.search.IndexStructure;
035: import org.apache.cocoon.components.search.components.AnalyzerManager;
036: import org.apache.cocoon.components.search.components.IndexManager;
037: import org.apache.cocoon.components.search.fieldmodel.DateFieldDefinition;
038: import org.apache.cocoon.components.search.fieldmodel.FieldDefinition;
039: import org.apache.cocoon.components.search.utils.SourceHelper;
040: import org.apache.cocoon.environment.Request;
041: import org.apache.excalibur.source.Source;
042: import org.apache.excalibur.source.SourceResolver;
043: import org.apache.excalibur.source.SourceUtil;
044: import org.apache.lenya.cms.cocoon.components.context.ContextUtility;
045: import org.apache.lenya.cms.publication.DocumentFactory;
046: import org.apache.lenya.cms.publication.DocumentUtil;
047: import org.apache.lenya.cms.publication.Publication;
048: import org.apache.lenya.cms.publication.PublicationException;
049: import org.apache.lenya.cms.publication.PublicationManager;
050:
051: /**
052: * Index Manager Component. Configure and Manage the differents indexes.
053: *
054: * @author Maisonneuve Nicolas
055: * @version 1.0
056: */
057: public class IndexManagerImpl extends AbstractLogEnabled implements
058: IndexManager, Serviceable, ThreadSafe, Configurable {
059:
060: /**
061: * indexer element
062: */
063: public static final String INDEXER_ELEMENT = "indexer";
064:
065: /**
066: * indexer element
067: */
068: public static final String INDEXER_ROLE_ATTRIBUTE = "role";
069:
070: /**
071: * set of indexes
072: */
073: public static final String INDEXES_ELEMENT = "indexes";
074:
075: /**
076: * Index declaration element
077: */
078: public static final String INDEX_ELEMENT = "index";
079:
080: /**
081: * default analyzer of a index
082: */
083: public static final String INDEX_DEFAULTANALZER_ATTRIBUTE = "analyzer";
084:
085: /**
086: * directory where the index is stored
087: */
088: public static final String INDEX_DIRECTORY_ATTRIBUTE = "directory";
089:
090: /**
091: * Index Structure element
092: */
093: public static final String STRUCTURE_ELEMENT = "structure";
094:
095: /**
096: * Field declaration element
097: */
098: public static final String FIELD_ELEMENT = "field";
099:
100: /**
101: * field name
102: */
103: public static final String ID_ATTRIBUTE = "id";
104:
105: /**
106: * type of the field: "text, "keyword", "date" (see
107: *
108: * @see org.apache.cocoon.components.search.fieldmodel.FieldDefinition class)
109: */
110: public static final String TYPE_ATTRIBUTE = "type";
111:
112: /**
113: * store information or not (true/false)
114: */
115: public static final String STORE_ATTRIBUTE = "storetext";
116:
117: /**
118: * The date Format when the field type is a date
119: */
120: public static final String DATEFORMAT_ATTRIBUTE = "dateformat";
121:
122: /**
123: * The name of the index configuration file.
124: */
125: public static final String INDEX_CONF_FILE = "search/lucene_index.xml";
126:
127: /**
128: * check the config file each time the getIndex is called to update if necessary the
129: * configuration
130: */
131: // public static final String CHECK_ATTRIBUTE = "check";
132: /**
133: * Source of the index configuration file
134: */
135: // public static final String CONFIG_ATTRIBUTE = "config";
136: /**
137: * Check or not the configuration file (automatic update if the file is changed)
138: */
139: // private boolean check;
140: /**
141: * Index configuration file
142: */
143: // private Source configfile;
144: private ServiceManager manager;
145:
146: private Map indexMap;
147:
148: protected Map indexes() {
149: if (this .indexMap == null) {
150: this .indexMap = new HashMap();
151: loadIndexes();
152: }
153: return this .indexMap;
154: }
155:
156: private String indexerRole = null;
157:
158: /*
159: * (non-Javadoc)
160: *
161: * @see org.apache.cocoon.components.search.components.IndexManager#contains(java.lang.String)
162: */
163: public boolean contains(String id) {
164: if (id != null) {
165: return this .indexes().get(id) != null;
166: }
167: return false;
168: }
169:
170: /*
171: * (non-Javadoc)
172: *
173: * @see org.apache.cocoon.components.search.components.IndexManager#getIndex(java.lang.String)
174: */
175: public Index getIndex(String id) throws IndexException {
176:
177: if (id == null || id.equals("")) {
178: throw new IndexException(" index with no name was called");
179: }
180:
181: Index index = (Index) this .indexes().get(id);
182: if (index == null) {
183: throw new IndexException("Index " + id
184: + " doesn't exist. Check if configuration "
185: + INDEX_CONF_FILE + " exists for this publication!");
186: }
187:
188: return index;
189: }
190:
191: /*
192: * (non-Javadoc)
193: *
194: * @see org.apache.cocoon.components.search.components.IndexManager#addIndex(org.apache.cocoon.components.search.Index)
195: */
196: public void addIndex(Index base) {
197: this .indexes().put(base.getID(), base);
198: }
199:
200: /*
201: * (non-Javadoc)
202: *
203: * @see org.apache.cocoon.components.search.components.IndexManager#remove(java.lang.String)
204: */
205: public void remove(String id) {
206: this .indexes().remove(id);
207: }
208:
209: protected void loadIndexes() {
210: // configure the index manager:
211:
212: // now check all publications and add their indexes:
213: PublicationManager pubManager = null;
214: SourceResolver resolver = null;
215: Source confSource = null;
216: ContextUtility util = null;
217: try {
218: util = (ContextUtility) this .manager
219: .lookup(ContextUtility.ROLE);
220: Request request = util.getRequest();
221: DocumentFactory factory = DocumentUtil.getDocumentFactory(
222: this .manager, request);
223: pubManager = (PublicationManager) this .manager
224: .lookup(PublicationManager.ROLE);
225: Publication[] publications = pubManager
226: .getPublications(factory);
227: resolver = (SourceResolver) this .manager
228: .lookup(SourceResolver.ROLE);
229:
230: for (int i = 0; i < publications.length; i++) {
231: String uri = "context://"
232: + Publication.PUBLICATION_PREFIX_URI + "/"
233: + publications[i].getId() + "/"
234: + Publication.CONFIGURATION_PATH + "/"
235: + INDEX_CONF_FILE;
236: confSource = resolver.resolveURI(uri);
237: if (confSource.exists()) {
238: addIndexes(confSource);
239: }
240: }
241: } catch (IOException e) {
242: throw new RuntimeException("Config file error", e);
243: } catch (ServiceException e) {
244: throw new RuntimeException(
245: "PublicationManager lookup error", e);
246: } finally {
247: if (pubManager != null) {
248: this .manager.release(pubManager);
249: }
250: if (resolver != null) {
251: if (confSource != null) {
252: resolver.release(confSource);
253: }
254: this .manager.release(resolver);
255: }
256: if (util != null) {
257: this .manager.release(util);
258: }
259: }
260:
261: getLogger().info("Search Engine - Index Manager configured.");
262: }
263:
264: /*
265: * (non-Javadoc)
266: *
267: * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
268: */
269: public void configure(Configuration configuration)
270: throws ConfigurationException {
271: this .indexerRole = configuration.getChild(INDEXER_ELEMENT)
272: .getAttribute(INDEXER_ROLE_ATTRIBUTE);
273: }
274:
275: /**
276: * Adds indexes from the given configuration file to the index manager.
277: * @param confSource
278: */
279: public void addIndexes(Source confSource) {
280: try {
281: Configuration indexConfiguration = SourceHelper
282: .build(confSource);
283: addIndexes(indexConfiguration);
284: } catch (ConfigurationException e) {
285: throw new RuntimeException("Error with configuration file "
286: + confSource.getURI(), e);
287: }
288: }
289:
290: /**
291: * Adds indexes from the given configuration object to the index manager.
292: * @param configuration
293: * @throws ConfigurationException
294: */
295: private void addIndexes(Configuration configuration)
296: throws ConfigurationException {
297: AnalyzerManager analyzerManager = null;
298: Configuration[] confs = configuration
299: .getChildren(INDEX_ELEMENT);
300:
301: if (confs.length == 0) {
302: throw new ConfigurationException("no index is defined !");
303: }
304: try {
305: analyzerManager = (AnalyzerManager) this .manager
306: .lookup(AnalyzerManager.ROLE);
307:
308: // configure each index
309: for (int i = 0; i < confs.length; i++) {
310: String id = confs[i].getAttribute(ID_ATTRIBUTE);
311: String analyzerid = confs[i]
312: .getAttribute(INDEX_DEFAULTANALZER_ATTRIBUTE);
313: String directory = confs[i]
314: .getAttribute(INDEX_DIRECTORY_ATTRIBUTE);
315: if (!analyzerManager.exist(analyzerid)) {
316: throw new ConfigurationException("Analyzer "
317: + analyzerid + " no found");
318: }
319:
320: Configuration[] fields = confs[i].getChild(
321: STRUCTURE_ELEMENT).getChildren(FIELD_ELEMENT);
322:
323: IndexStructure docdecl = new IndexStructure();
324: for (int j = 0; j < fields.length; j++) {
325:
326: FieldDefinition fielddecl;
327:
328: // field id attribute
329: String id_field = fields[j]
330: .getAttribute(ID_ATTRIBUTE);
331:
332: // field type attribute
333: String typeS = fields[j].getAttribute(
334: TYPE_ATTRIBUTE, "");
335: int type = FieldDefinition.stringTotype(typeS);
336: try {
337: fielddecl = FieldDefinition.create(id_field,
338: type);
339: } catch (IllegalArgumentException e) {
340: throw new ConfigurationException("field "
341: + id_field + " type " + typeS, e);
342: }
343:
344: // field store attribute
345: boolean store;
346: if (fielddecl.getType() == FieldDefinition.TEXT) {
347: store = fields[j].getAttributeAsBoolean(
348: STORE_ATTRIBUTE, false);
349: } else {
350: store = fields[j].getAttributeAsBoolean(
351: STORE_ATTRIBUTE, true);
352: }
353: fielddecl.setStore(store);
354:
355: // field dateformat attribute
356: if (fielddecl.getType() == FieldDefinition.DATE) {
357: String dateformat_field = fields[j]
358: .getAttribute(DATEFORMAT_ATTRIBUTE);
359: ((DateFieldDefinition) fielddecl)
360: .setDateFormat(new SimpleDateFormat(
361: dateformat_field));
362: }
363:
364: this .getLogger().debug("field added: " + fielddecl);
365: docdecl.addFieldDef(fielddecl);
366: }
367:
368: Index index = new Index();
369: index.setID(id);
370: index.setIndexer(indexerRole);
371:
372: // if the directory path is relative, prepend context path:
373: if (!directory.startsWith(File.separator)) {
374: directory = getServletContextPath()
375: + File.separator + directory;
376: }
377:
378: if (index.setDirectory(directory)) {
379: this .getLogger().warn(
380: "directory " + directory + " was locked ");
381: }
382: index.setDefaultAnalyzerID(analyzerid);
383: index.setStructure(docdecl);
384: index.setManager(manager);
385:
386: this .addIndex(index);
387: this .getLogger().info(
388: "add index " + index.getID()
389: + " for directory " + directory);
390: }
391: } catch (ServiceException e) {
392: throw new ConfigurationException(
393: "AnalyzerManager lookup error", e);
394: } catch (Exception e) {
395: throw new ConfigurationException(e.getMessage(), e);
396: } finally {
397: if (analyzerManager != null) {
398: this .manager.release(analyzerManager);
399: }
400: }
401: }
402:
403: /**
404: * @return The servlet context path.
405: * @throws Exception if an error occurs.
406: */
407: public String getServletContextPath() throws Exception {
408: SourceResolver resolver = null;
409: Source source = null;
410: try {
411: resolver = (SourceResolver) this .manager
412: .lookup(SourceResolver.ROLE);
413: source = resolver.resolveURI("context:///");
414: return SourceUtil.getFile(source).getCanonicalPath();
415: } finally {
416: if (resolver != null) {
417: if (source != null) {
418: resolver.release(source);
419: }
420: this .manager.release(resolver);
421: }
422: }
423: }
424:
425: /*
426: * (non-Javadoc)
427: * @see org.apache.cocoon.components.search.components.IndexManager#getIndex()
428: */
429: public Index[] getIndex() {
430: return (Index[]) this .indexes().values().toArray(
431: new Index[indexes().size()]);
432: }
433:
434: /*
435: * (non-Javadoc)
436: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
437: */
438: public void service(ServiceManager manager) throws ServiceException {
439: this.manager = manager;
440: }
441:
442: }
|