Java tutorial
/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.runtime.search.lucene2; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import lucee.commons.io.SystemUtil; import lucee.commons.io.log.Log; import lucee.commons.io.res.Resource; import lucee.commons.io.res.ResourcesImpl; import lucee.commons.io.res.filter.DirectoryResourceFilter; import lucee.commons.io.res.filter.ResourceFilter; import lucee.commons.io.res.filter.ResourceNameFilter; import lucee.commons.io.res.util.FileWrapper; import lucee.commons.io.res.util.ResourceUtil; import lucee.commons.lang.SerializableObject; import lucee.commons.lang.StringUtil; import lucee.runtime.config.ConfigImpl; import lucee.runtime.op.Caster; import lucee.runtime.search.AddionalAttrs; import lucee.runtime.search.IndexResult; import lucee.runtime.search.IndexResultImpl; import lucee.runtime.search.SearchCollectionSupport; import lucee.runtime.search.SearchData; import lucee.runtime.search.SearchEngineSupport; import lucee.runtime.search.SearchException; import lucee.runtime.search.SearchIndex; import lucee.runtime.search.SearchResulItem; import lucee.runtime.search.SearchResulItemImpl; import lucee.runtime.search.SuggestionItem; import lucee.runtime.search.lucene2.docs.CustomDocument; import lucee.runtime.search.lucene2.highlight.Highlight; import lucee.runtime.search.lucene2.net.WebCrawler; import lucee.runtime.search.lucene2.query.Literal; import lucee.runtime.search.lucene2.query.Op; import lucee.runtime.type.QueryColumn; import lucee.runtime.type.Struct; import lucee.runtime.type.StructImpl; import lucee.runtime.type.dt.DateTime; import lucee.runtime.type.util.ListUtil; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.Searcher; import org.apache.lucene.search.spell.Dictionary; import org.apache.lucene.search.spell.LuceneDictionary; import org.apache.lucene.search.spell.SpellChecker; import org.apache.lucene.store.FSDirectory; /** * */ public final class LuceneSearchCollection extends SearchCollectionSupport { private static final long serialVersionUID = 3430238280421965781L; private Resource collectionDir; private boolean spellcheck; private Log log; private static final SerializableObject token = new SerializableObject(); /** * @param searchEngine * @param name * @param path * @param language * @param lastUpdate * @param created */ public LuceneSearchCollection(SearchEngineSupport searchEngine, String name, Resource path, String language, //int count, DateTime lastUpdate, DateTime created, boolean spellcheck) { super(searchEngine, name, path, language, lastUpdate, created); this.spellcheck = spellcheck; collectionDir = getPath().getRealResource(StringUtil.toIdentityVariableName(getName())); log = ((ConfigImpl) searchEngine.getConfig()).getLog("search"); } public LuceneSearchCollection(SearchEngineSupport searchEngine, String name, Resource path, String language, //int count, DateTime lastUpdate, DateTime created) { this(searchEngine, name, path, language, lastUpdate, created, true); } @Override protected void _create() throws SearchException { try { if (!collectionDir.exists()) collectionDir.createDirectory(true); } catch (IOException e) { } } @Override protected void _optimize() throws SearchException { IndexWriter[] writers = _getWriters(false); for (int i = 0; i < writers.length; i++) { try { optimizeEL(writers[i]); } finally { close(writers[i]); } } } @Override protected void _map(Resource path) throws SearchException { throw new SearchException("mapping of existing Collection for file [" + path + "] not supported"); } @Override protected void _repair() throws SearchException { //throw new SearchException("repair of existing Collection not supported"); } @Override protected IndexResult _indexFile(String id, String title, Resource res, String language) throws SearchException { info(res.getAbsolutePath()); _checkLanguage(language); int before = getDocumentCount(id); IndexWriter writer = null; synchronized (token) { try { writer = _getWriter(id, true); _index(writer, res, res.getName()); writer.optimize(); } catch (Exception e) { throw new SearchException(e); } finally { close(writer); } indexSpellCheck(id); } if (getDocumentCount(id) == before) return new IndexResultImpl(0, 0, 1); return new IndexResultImpl(0, 1, 0); } @Override protected IndexResult _indexPath(String id, String title, Resource dir, String[] extensions, boolean recurse, String language) throws SearchException { info(dir.getAbsolutePath()); _checkLanguage(language); int doccount = 0; IndexWriter writer = null; synchronized (token) { try { writer = _getWriter(id, true); doccount = _list(0, writer, dir, new LuceneExtensionFileFilter(extensions, recurse), ""); //optimizeEL(writer); writer.optimize(); } catch (IOException e) { throw new SearchException(e); } finally { close(writer); } indexSpellCheck(id); } return new IndexResultImpl(0, 0, doccount); } private void optimizeEL(IndexWriter writer) { if (writer == null) return; try { writer.optimize(); } catch (Throwable t) { //print.printST(t); } } private void indexSpellCheck(String id) throws SearchException { if (!spellcheck) return; IndexReader reader = null; FSDirectory spellDir = null; Resource dir = _createSpellDirectory(id); try { File spellFile = FileWrapper.toFile(dir); spellDir = FSDirectory.getDirectory(spellFile); reader = _getReader(id, false); Dictionary dictionary = new LuceneDictionary(reader, "contents"); SpellChecker spellChecker = new SpellChecker(spellDir); spellChecker.indexDictionary(dictionary); } catch (IOException ioe) { throw new SearchException(ioe); } finally { flushEL(reader); closeEL(reader); } } private void close(IndexWriter writer) throws SearchException { if (writer != null) { //print.out("w-close"); try { writer.close(); } catch (IOException e) { throw new SearchException(e); } } } private static void close(IndexReader reader) throws SearchException { if (reader != null) { try { reader.close(); } catch (IOException e) { throw new SearchException(e); } } } private static void close(Searcher searcher) throws SearchException { if (searcher != null) { try { searcher.close(); } catch (IOException e) { throw new SearchException(e); } } } private static void flushEL(IndexReader reader) { //print.out("r-closeEL"); if (reader != null) { try { reader.flush(); } catch (Throwable t) { //throw new SearchException(t); } } } private static void closeEL(IndexReader reader) { //print.out("r-closeEL"); if (reader != null) { try { reader.close(); } catch (Throwable t) { //throw new SearchException(t); } } } @Override protected IndexResult _indexURL(String id, String title, URL url, String[] extensions, boolean recurse, String language) throws SearchException { //timeout=ThreadLocalPageContext.getConfig().getRequestTimeout().getMillis(); return _indexURL(id, title, url, extensions, recurse, language, 50000L); } public IndexResult _indexURL(String id, String title, URL url, String[] extensions, boolean recurse, String language, long timeout) throws SearchException { _checkLanguage(language); info(url.toExternalForm()); int before = getDocumentCount(id); IndexWriter writer = null; synchronized (token) { try { writer = _getWriter(id, true); new WebCrawler(log).parse(writer, url, extensions, recurse, timeout); writer.optimize(); } catch (Exception e) { throw new SearchException(e); } finally { close(writer); } indexSpellCheck(id); } if (getDocumentCount(id) == before) return new IndexResultImpl(0, 0, 1); return new IndexResultImpl(0, 1, 0); //throw new SearchException("url indexing not supported"); } /** * @param id * @param title * @param keyColumn * @param bodyColumns * @param language * @param custom1 * @param custom2 * @param custom3 * @param custom4 * @return * @throws SearchException */ protected IndexResult _deleteCustom(String id, QueryColumn keyColumn) throws SearchException { int countBefore = 0; int countAfter = 0; Map<String, Document> docs = new HashMap<String, Document>(); Set<String> keys = toSet(keyColumn); IndexWriter writer = null; String key; IndexReader reader = null; Document doc; synchronized (token) { try { try { reader = _getReader(id, false); countBefore = reader.maxDoc(); for (int i = 0; i < countBefore; i++) { doc = reader.document(i); key = doc.getField("key").stringValue(); if (!keys.contains(key)) docs.put(key, doc); } } catch (Exception e) { } finally { close(reader); } countAfter = docs.size(); writer = _getWriter(id, true); Iterator<Entry<String, Document>> it = docs.entrySet().iterator(); while (it.hasNext()) { writer.addDocument(it.next().getValue()); } optimizeEL(writer); } catch (IOException e) { throw new SearchException(e); } finally { close(writer); } indexSpellCheck(id); } int removes = countBefore - countAfter; return new IndexResultImpl(removes, 0, 0); } private Set<String> toSet(QueryColumn column) { Set<String> set = new HashSet<String>(); Iterator it = column.valueIterator(); while (it.hasNext()) { set.add(Caster.toString(it.next(), null)); } return set; } /** * @param id * @param title * @param keyColumn * @param bodyColumns * @param language * @param custom1 * @param custom2 * @param custom3 * @param custom4 * @return * @throws SearchException */ protected IndexResult _indexCustom(String id, Object title, QueryColumn keyColumn, QueryColumn[] bodyColumns, String language, Object urlpath, Object custom1, Object custom2, Object custom3, Object custom4) throws SearchException { _checkLanguage(language); String t; String url; String c1; String c2; String c3; String c4; int countExisting = 0; int countAdd = keyColumn.size(); int countNew = 0; Map<String, Document> docs = new HashMap<String, Document>(); IndexWriter writer = null; synchronized (token) { try { // read existing reader IndexReader reader = null; try { reader = _getReader(id, false); int len = reader.maxDoc(); Document doc; for (int i = 0; i < len; i++) { doc = reader.document(i); docs.put(doc.getField("key").stringValue(), doc); } } catch (Exception e) { } finally { close(reader); } countExisting = docs.size(); writer = _getWriter(id, true); int len = keyColumn.size(); String key; for (int i = 1; i <= len; i++) { key = Caster.toString(keyColumn.get(i, null), null); if (key == null) continue; StringBuilder body = new StringBuilder(); for (int y = 0; y < bodyColumns.length; y++) { Object tmp = bodyColumns[y].get(i, null); if (tmp != null) { body.append(tmp.toString()); body.append(' '); } } //t=(title==null)?null:Caster.toString(title.get(i,null),null); //url=(urlpath==null)?null:Caster.toString(urlpath.get(i,null),null); t = getRow(title, i); url = getRow(urlpath, i); c1 = getRow(custom1, i); c2 = getRow(custom2, i); c3 = getRow(custom3, i); c4 = getRow(custom4, i); docs.put(key, CustomDocument.getDocument(t, key, body.toString(), url, c1, c2, c3, c4)); } countNew = docs.size(); Iterator<Entry<String, Document>> it = docs.entrySet().iterator(); Entry<String, Document> entry; Document doc; while (it.hasNext()) { entry = it.next(); doc = entry.getValue(); writer.addDocument(doc); } optimizeEL(writer); //writer.optimize(); } catch (IOException ioe) { throw new SearchException(ioe); } finally { close(writer); } indexSpellCheck(id); } int inserts = countNew - countExisting; return new IndexResultImpl(0, inserts, countAdd - inserts); } private String getRow(Object column, int row) { if (column instanceof QueryColumn) { return Caster.toString(((QueryColumn) column).get(row, null), null); } if (column != null) return Caster.toString(column, null); return null; } @Override protected IndexResult _purge() throws SearchException { SearchIndex[] indexes = getIndexes(); int count = 0; for (int i = 0; i < indexes.length; i++) { count += getDocumentCount(indexes[i].getId()); } ResourceUtil.removeChildrenEL(collectionDir); return new IndexResultImpl(count, 0, 0); } @Override protected IndexResult _delete() throws SearchException { SearchIndex[] indexes = getIndexes(); int count = 0; for (int i = 0; i < indexes.length; i++) { count += getDocumentCount(indexes[i].getId()); } ResourceUtil.removeEL(collectionDir, true); return new IndexResultImpl(count, 0, 0); } @Override protected IndexResult _deleteIndex(String id) throws SearchException { int count = getDocumentCount(id); ResourceUtil.removeEL(_getIndexDirectory(id, true), true); return new IndexResultImpl(count, 0, 0); } @Override public SearchResulItem[] _search(SearchData data, String criteria, String language, short type, String categoryTree, String[] category) throws SearchException { try { if (type != SEARCH_TYPE_SIMPLE) throw new SearchException("search type explicit not supported"); Analyzer analyzer = SearchUtil.getAnalyzer(language); Query query = null; Op op = null; Object highlighter = null; lucee.runtime.search.lucene2.query.QueryParser queryParser = new lucee.runtime.search.lucene2.query.QueryParser(); AddionalAttrs aa = AddionalAttrs.getAddionlAttrs(); aa.setHasRowHandling(true); int startrow = aa.getStartrow(); int maxrows = aa.getMaxrows(); if (!criteria.equals("*")) { // FUTURE take this data from calling parameters op = queryParser.parseOp(criteria); if (op == null) criteria = "*"; else criteria = op.toString(); try { query = new QueryParser("contents", analyzer).parse(criteria); highlighter = Highlight.createHighlighter(query, aa.getContextHighlightBegin(), aa.getContextHighlightEnd()); } catch (ParseException e) { throw new SearchException(e); } } Resource[] files = _getIndexDirectories(); if (files == null) return new SearchResulItem[0]; ArrayList<SearchResulItem> list = new ArrayList<SearchResulItem>(); String ct, c; ArrayList<String> spellCheckIndex = spellcheck ? new ArrayList<String>() : null; int count = 0; IndexReader reader = null; Searcher searcher = null; try { outer: for (int i = 0; i < files.length; i++) { if (removeCorrupt(files[i])) continue; String strFile = files[i].toString(); SearchIndex si = indexes.get(files[i].getName()); if (si == null) continue; ct = si.getCategoryTree(); c = ListUtil.arrayToList(si.getCategories(), ","); // check category tree if (!matchCategoryTree(ct, categoryTree)) continue; if (!matchCategories(si.getCategories(), category)) continue; Document doc; String id = files[i].getName(); data.addRecordsSearched(_countDocs(strFile)); reader = _getReader(id, false); if (query == null && "*".equals(criteria)) { int len = reader.numDocs(); for (int y = 0; y < len; y++) { if (startrow > ++count) continue; if (maxrows > -1 && list.size() >= maxrows) break outer; doc = reader.document(y); list.add(createSearchResulItem(highlighter, analyzer, doc, id, 1, ct, c, aa.getContextPassages(), aa.getContextBytes())); } } else { if (spellcheck) spellCheckIndex.add(id); // search searcher = new IndexSearcher(reader); Hits hits = searcher.search(query); int len = hits.length(); for (int y = 0; y < len; y++) { if (startrow > ++count) continue; if (maxrows > -1 && list.size() >= maxrows) break outer; //list.add(new SearchResulItemHits(hits,y,highlighter,analyzer,id,ct,c,aa.getContextPassages(),aa.getContextBytes())); doc = hits.doc(y); list.add(createSearchResulItem(highlighter, analyzer, doc, id, hits.score(y), ct, c, aa.getContextPassages(), aa.getContextBytes())); } } } } finally { close(reader); close(searcher); } // spellcheck //SearchData data=ThreadLocalSearchData.get(); if (spellcheck && data != null) { if (data.getSuggestionMax() >= list.size()) { Map suggestions = data.getSuggestion(); Iterator it = spellCheckIndex.iterator(); String id; Literal[] literals = queryParser.getLiteralSearchedTerms(); String[] strLiterals = queryParser.getStringSearchedTerms(); boolean setSuggestionQuery = false; while (it.hasNext()) { id = (String) it.next(); // add to set to remove duplicate values SuggestionItem si; SpellChecker sc = getSpellChecker(id); for (int i = 0; i < strLiterals.length; i++) { String[] arr = sc.suggestSimilar(strLiterals[i], 1000); if (arr.length > 0) { literals[i].set("<suggestion>" + arr[0] + "</suggestion>"); setSuggestionQuery = true; si = (SuggestionItem) suggestions.get(strLiterals[i]); if (si == null) suggestions.put(strLiterals[i], new SuggestionItem(arr)); else si.add(arr); } } } if (setSuggestionQuery) data.setSuggestionQuery(op.toString()); } } return list.toArray(new SearchResulItem[list.size()]); } catch (IOException e) { throw new SearchException(e); } } private SpellChecker getSpellChecker(String id) throws IOException { FSDirectory siDir = FSDirectory.getDirectory(FileWrapper.toFile(_getSpellDirectory(id))); SpellChecker spellChecker = new SpellChecker(siDir); return spellChecker; } private boolean removeCorrupt(Resource dir) { if (ResourceUtil.isEmptyFile(dir)) { ResourceUtil.removeEL(dir, true); return true; } return false; } private static SearchResulItem createSearchResulItem(Object highlighter, Analyzer a, Document doc, String name, float score, String ct, String c, int maxNumFragments, int maxLength) { String contextSummary = ""; if (maxNumFragments > 0) contextSummary = Highlight.createContextSummary(highlighter, a, doc.get("contents"), maxNumFragments, maxLength, doc.get("summary")); String summary = doc.get("summary"); return new SearchResulItemImpl(name, doc.get("title"), score, doc.get("key"), doc.get("url"), summary, contextSummary, ct, c, doc.get("custom1"), doc.get("custom2"), doc.get("custom3"), doc.get("custom4"), doc.get("mime-type"), doc.get("author"), doc.get("size")); } private boolean matchCategories(String[] categoryIndex, String[] categorySearch) { if (categorySearch == null || categorySearch.length == 0) return true; String search; for (int s = 0; s < categorySearch.length; s++) { search = categorySearch[s]; for (int i = 0; i < categoryIndex.length; i++) { if (search.equals(categoryIndex[i])) return true; } } return false; } private boolean matchCategoryTree(String categoryTreeIndex, String categoryTreeSearch) { //if(StringUtil.isEmpty(categoryTreeIndex) || categoryTreeIndex.equals("/")) return true; //if(StringUtil.isEmpty(categoryTreeSearch) || categoryTreeSearch.equals("/")) return true; return categoryTreeIndex.startsWith(categoryTreeSearch); } /** * list a directory and call every file * @param writer * @param res * @param filter * @param url * @throws IOException * @throws InterruptedException */ private int _list(int doccount, IndexWriter writer, Resource res, ResourceFilter filter, String url) { if (res.isReadable()) { if (res.exists() && res.isDirectory()) { Resource[] files = (filter == null) ? res.listResources() : res.listResources(filter); if (files != null) { for (int i = 0; i < files.length; i++) { if (removeCorrupt(files[i])) { continue; } doccount = _list(doccount, writer, files[i], filter, url + "/" + files[i].getName()); } } } else { try { info(res.getAbsolutePath()); _index(writer, res, url); doccount++; } catch (Exception e) { } } } return doccount; } /** * index a single file * @param writer * @param file * @param url * @throws IOException * @throws InterruptedException */ private void _index(IndexWriter writer, Resource file, String url) throws IOException { if (!file.exists()) return; writer.addDocument(DocumentUtil.toDocument(file, url, SystemUtil.getCharset().name())); } /** * @param id * @return returns the Index Directory */ private Resource _getIndexDirectory(String id, boolean createIfNotExists) { Resource indexDir = collectionDir.getRealResource(id); if (createIfNotExists && !indexDir.exists()) indexDir.mkdirs(); return indexDir; } /** * get writer to id * @param id * @return returns the Writer * @throws IOException * @throws SearchException * @throws IOException */ private IndexWriter _getWriter(String id, boolean create) throws SearchException, IOException { // FUTURE support for none file -> Directory Object Resource dir = _getIndexDirectory(id, true); return new IndexWriter(FileWrapper.toFile(dir), SearchUtil.getAnalyzer(getLanguage()), create); //return new ResourceIndexWriter(dir, SearchUtil.getAnalyzer(getLanguage()), create); /*try { return new ResourceIndexWriter(dir, SearchUtil.getAnalyzer(getLanguage()), true); } catch (IOException e) { ResourceUtil.removeChildrenEL(dir); dir.getResourceProvider().unlock(dir); return new ResourceIndexWriter(dir, SearchUtil.getAnalyzer(getLanguage()),true); }*/ } private IndexReader _getReader(String id, boolean absolute) throws IOException { return _getReader(_getFile(id, absolute)); } private IndexReader _getReader(File file) throws IOException { if (!IndexReader.indexExists(file)) throw new IOException("there is no index in [" + file + "]"); return IndexReader.open(file); } private File _getFile(String id, boolean absolute) throws IOException { Resource res = absolute ? ResourcesImpl.getFileResourceProvider().getResource(id) : _getIndexDirectory(id, true); res.getResourceProvider().read(res); return FileWrapper.toFile(res); } /** * @return returns all existing IndexWriter */ private Resource[] _getIndexDirectories() { Resource[] files = collectionDir.listResources(new DirectoryResourceFilter()); return files; } /** * @return returns all existing IndexWriter * @throws SearchException */ private IndexWriter[] _getWriters(boolean create) throws SearchException { Resource[] files = _getIndexDirectories(); if (files == null) return new IndexWriter[0]; IndexWriter[] writers = new IndexWriter[files.length]; for (int i = 0; i < files.length; i++) { try { writers[i] = _getWriter(files[i].getName(), create); } catch (IOException e) { } } return writers; } private int _countDocs(String col) { // FUTURE add support for none file resources int totalDocs; IndexReader reader = null; try { reader = _getReader(col, true); totalDocs = reader.numDocs(); } catch (Exception e) { return 0; } finally { closeEL(reader); } return totalDocs; } /** * @deprecated see SearchUtil.getAnalyzer(String language); * @param language * @return returns language matching Analyzer * @throws SearchException */ public static Analyzer _getAnalyzer(String language) throws SearchException { return SearchUtil.getAnalyzer(language); } /** * check given language against collection language * @param language * @throws SearchException */ private void _checkLanguage(String language) throws SearchException { if (language != null && !language.trim().equalsIgnoreCase(getLanguage())) { throw new SearchException( "collection Language and Index Language must be of same type, but collection language is of type [" + getLanguage() + "] and index language is of type [" + language + "]"); } } @Override public int getDocumentCount(String id) { try { if (!_getIndexDirectory(id, false).exists()) return 0; IndexReader r = null; int num = 0; try { r = _getReader(id, false); num = r.numDocs(); } finally { close(r); } return num; } catch (Exception e) { } return 0; } @Override public int getDocumentCount() { int count = 0; SearchIndex[] _indexes = getIndexes(); for (int i = 0; i < _indexes.length; i++) { count += getDocumentCount(_indexes[i].getId()); } return count; } @Override public long getSize() { return ResourceUtil.getRealSize(collectionDir) / 1024; } public Object getCategoryInfo() { Struct categories = new StructImpl(); Struct categorytrees = new StructImpl(); Struct info = new StructImpl(); info.setEL("categories", categories); info.setEL("categorytrees", categorytrees); Iterator it = indexes.keySet().iterator(); String[] cats; String catTree; Double tmp; while (it.hasNext()) { SearchIndex index = indexes.get(it.next()); // category tree catTree = index.getCategoryTree(); tmp = (Double) categorytrees.get(catTree, null); if (tmp == null) categorytrees.setEL(catTree, Caster.toDouble(1)); else categorytrees.setEL(catTree, Caster.toDouble(tmp.doubleValue() + 1)); // categories cats = index.getCategories(); for (int i = 0; i < cats.length; i++) { tmp = (Double) categories.get(cats[i], null); if (tmp == null) categories.setEL(cats[i], Caster.toDouble(1)); else categories.setEL(cats[i], Caster.toDouble(tmp.doubleValue() + 1)); } } return info; } class ResourceIndexWriter extends IndexWriter { private Resource dir; public ResourceIndexWriter(Resource dir, Analyzer analyzer, boolean create) throws IOException { super(FileWrapper.toFile(dir), analyzer, create); this.dir = dir; dir.getResourceProvider().lock(dir); } @Override public synchronized void close() throws IOException { super.close(); dir.getResourceProvider().unlock(dir); } } private Resource _createSpellDirectory(String id) { Resource indexDir = collectionDir.getRealResource(id + "_" + (_getMax(true) + 1) + "_spell"); //print.out("create:"+indexDir); indexDir.mkdirs(); return indexDir; } private Resource _getSpellDirectory(String id) { Resource indexDir = collectionDir.getRealResource(id + "_" + _getMax(false) + "_spell"); //print.out("get:"+indexDir); return indexDir; } private long _getMax(boolean delete) { Resource[] children = collectionDir.listResources(new SpellDirFilter()); long max = 0, nbr; String name; for (int i = 0; i < children.length; i++) { name = children[i].getName(); name = name.substring(0, name.length() - 6); nbr = Caster.toLongValue(name.substring(name.lastIndexOf('_') + 1), 0); if (delete) { try { children[i].remove(true); continue; } catch (Throwable t) { } } if (nbr > max) max = nbr; } return max; } private void info(String doc) { if (log == null) return; log.log(Log.LEVEL_INFO, "Collection:" + getName(), "indexing " + doc); } public class SpellDirFilter implements ResourceNameFilter { @Override public boolean accept(Resource parent, String name) { return name.endsWith("_spell"); } } }