/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/search/trunk/search-impl/impl/src/java/org/sakaiproject/search/journal/impl/RefCountMultiReader.java $ * $Id: RefCountMultiReader.java 105078 2012-02-24 23:00:38Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.search.journal.impl; import java.io.File; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.sakaiproject.search.journal.api.IndexCloser; import org.sakaiproject.search.journal.api.JournaledIndex; import org.sakaiproject.search.journal.api.ManagementOperation; import org.sakaiproject.search.journal.api.ThreadBinder; import org.sakaiproject.search.util.FileUtils; import org.sakaiproject.thread_local.api.ThreadBound; import org.sakaiproject.thread_local.api.ThreadLocalManager; /** * @author ieb */ public class RefCountMultiReader extends MultiReader implements ThreadBound, ThreadBinder, IndexCloser { private static final Log log = LogFactory.getLog(RefCountMultiReader.class); private IndexReader[] indexReaders; private JournaledFSIndexStorage storage; private int count = 0; private boolean doclose = false; private boolean closing = false; private ThreadLocalManager threadLocalManager; private ConcurrentHashMap<Object, Object> parents = new ConcurrentHashMap<Object, Object>(); private ThreadLocal<String> unbindingMonitor = new ThreadLocal<String>(); private Object closeMonitor = new Object(); private ManagementOperation managementOperation; private static int opened = 0; /** * @param arg0 * @param storage * @throws IOException */ public RefCountMultiReader(IndexReader[] indexReaders, JournaledFSIndexStorage storage) throws IOException { super(indexReaders); this.managementOperation = ConcurrentIndexManager.getCurrentManagementOperation(); opened++; this.indexReaders = indexReaders; this.storage = storage; } /* * (non-Javadoc) * * @see org.apache.lucene.index.MultiReader#doClose() */ @Override protected synchronized void doClose() throws IOException { doclose = true; unbind(); storage.fireIndexReaderClose(this); } /** * */ public boolean doFinalClose() { log.debug("doFinalClose() on " + this); if (canClose()) { return forceClose(); } return false; } public boolean forceClose() { log.debug("forceClose() on " + this); synchronized (closeMonitor) { if (closing) return true; closing = true; } opened--; if (log.isDebugEnabled()) log.debug("Closing Index " + this); for (IndexReader ir : indexReaders) { try { // close index File f = null; boolean deleteme = false; try { // can we delete any of these indexes ? Directory d = ir.directory(); // index closers manage their own delete operations if (!(ir instanceof IndexCloser) && (d instanceof FSDirectory)) { FSDirectory fsd = (FSDirectory) d; f = fsd.getFile(); File deleteMarker = new File(f,JournaledIndex.DELETE_ON_CLOSE_FILE); deleteme = deleteMarker.exists(); } ir.close(); if (log.isDebugEnabled()) log.debug("Closed indexreader " + ir); } catch (AlreadyClosedException acex) { log.debug("Already closed"); } if (f != null && deleteme) { FileUtils.deleteAll(f); log.debug("Deleting Index on Close " + f); } } catch (IOException ioex) { log.debug(ioex); } } try { super.doClose(); } catch (IOException ex) { log.debug(ex); } try { Directory d = this.directory(); if (d instanceof FSDirectory) { FSDirectory fsd = (FSDirectory) d; File f = fsd.getFile(); File deleteMarker = new File(f,JournaledIndex.DELETE_ON_CLOSE_FILE); if (deleteMarker.exists()) { FileUtils.deleteAll(f); log.debug("Deleting Index on Close " + f); } } } catch (AlreadyClosedException acex) { log.debug("Already closed"); } catch (UnsupportedOperationException uoex) { log.debug(uoex); } catch (IOException ioex) { log.debug(ioex); } return true; } /** * The isCurrent method in 1.9.1 has a NPE bug, this fixes it * * @see org.apache.lucene.index.IndexReader#isCurrent() */ @Override public boolean isCurrent() throws IOException { for (IndexReader ir : indexReaders) { if (!ir.isCurrent()) return false; } return true; } /* * (non-Javadoc) * * @see org.sakaiproject.thread_local.api.ThreadBound#unbind() */ public void unbind() { Object unbinding = unbindingMonitor.get(); if (unbinding == null) { try { unbindingMonitor.set("unbinding"); if (threadLocalManager != null) { Object o = threadLocalManager.get(String.valueOf(this)); if (o != null) { count--; if (log.isDebugEnabled()) log.debug("Unbound " + this + " " + count); threadLocalManager.set(String.valueOf(this), null); // unbind // the // dependents } for (IndexReader ir : indexReaders) { if (ir instanceof ThreadBound) { log.debug("Calling unbind for " + ir); ((ThreadBound) ir).unbind(); } } } if (canClose()) { log.debug("Calling forceClose for " + this); forceClose(); } } finally { unbindingMonitor.set(null); } } } public void bind(ThreadLocalManager tlm) { threadLocalManager = tlm; Object o = tlm.get(String.valueOf(this)); if (o == null) { count++; tlm.set(String.valueOf(this), this); if (log.isDebugEnabled()) log.debug("Bind " + this + " " + count); } else if (o != this) { log.warn(" More than one object bound to the same key "); } for (IndexReader ir : indexReaders) { if (ir instanceof ThreadBinder) { ((ThreadBinder) ir).bind(tlm); } } } /* * (non-Javadoc) * * @see org.sakaiproject.search.journal.impl.IndexCloser#canClose() */ public boolean canClose() { return (count <= 0 && doclose && parents.size() == 0); } /* * (non-Javadoc) * * @see org.sakaiproject.search.journal.api.IndexCloser#addParent(org.apache.lucene.search.IndexSearcher) */ public void addParent(Object searcher) { parents.put(searcher, searcher); } /* * (non-Javadoc) * * @see org.sakaiproject.search.journal.api.IndexCloser#removeParent(java.lang.Object) */ public void removeParent(Object searcher) { parents.remove(searcher); } /** * @return */ public static int getOpened() { return opened; } /* * (non-Javadoc) * * @see org.sakaiproject.search.journal.api.IndexCloser#getName() */ public String getName() { StringBuilder sb = new StringBuilder(); sb.append(managementOperation).append(" "); sb.append(toString()).append(" RefCount:").append(count); for (IndexReader ir : indexReaders) { sb.append("[").append(ir.directory().toString()).append("]"); } return sb.toString(); } }