/* See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * Esri Inc. licenses this file to You under the Apache 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.apache.org/licenses/LICENSE-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 com.esri.gpt.catalog.lucene; import com.esri.gpt.framework.collection.StringAttributeMap; import com.esri.gpt.framework.context.RequestContext; import com.esri.gpt.framework.scheduler.IScheduledTask; import com.esri.gpt.server.assertion.AsnConfig; import com.esri.gpt.server.assertion.AsnFactory; import com.esri.gpt.server.assertion.index.AsnIndexAdapter; import com.esri.gpt.server.assertion.index.AsnIndexReference; import com.esri.gpt.server.assertion.index.AsnIndexReferences; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.store.Lock; import org.apache.lucene.store.LockObtainFailedException; /** * Background thread to optimize the Lucene index. */ public class LuceneIndexOptimizer implements Runnable, IScheduledTask { /** class variables ========================================================= */ private static Logger LOGGER = Logger.getLogger(LuceneIndexOptimizer.class.getName()); /** instance variables ====================================================== */ private StringAttributeMap parameters = null; private boolean wasInterrupted = false; /** constructors =========================================================== */ /** Default constructor. */ public LuceneIndexOptimizer() {} /** properties ============================================================= */ /** * Sets the configuration paramaters for the task. * @param parameters the configuration paramaters */ public void setParameters(StringAttributeMap parameters) { this.parameters = parameters; } /** methods ================================================================= */ /** * Checks to see if the thread was interrupted. * @return true if the thread was interrupted */ private boolean checkInterrupted() { if (!this.wasInterrupted) { if (Thread.interrupted()) { this.wasInterrupted = true; } } return this.wasInterrupted; } /** * Run the optimization process. */ public void run() { LOGGER.info("Optimization run started..."); RequestContext context = null; IndexWriter writer = null; Lock backgroundLock = null; long tStartMillis = System.currentTimeMillis(); try { // initialize context = RequestContext.extract(null); LuceneIndexAdapter adapter = new LuceneIndexAdapter(context); adapter.touch(); // ensures that a proper directory structure exists if (this.checkInterrupted()) return; // obtain the background thread lock, // sleep for 10 minutes if busy then try again try { backgroundLock = adapter.obtainBackgroundLock(); } catch (LockObtainFailedException lofe) { if (this.checkInterrupted()) return; try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { throw new IOException(e.toString()); } if (this.checkInterrupted()) return; backgroundLock = adapter.obtainBackgroundLock(); } // optimize the index writer = adapter.newWriter(); if (this.checkInterrupted()) return; writer.optimize(); adapter.closeWriter(writer); writer = null; // log the summary message double dSec = (System.currentTimeMillis() - tStartMillis) / 1000.0; StringBuffer msg = new StringBuffer(); msg.append("Optimization run completed."); msg.append(", runtime: "); msg.append(Math.round(dSec / 60.0 * 100.0) / 100.0).append(" minutes"); if (dSec <= 600) { msg.append(", ").append(Math.round(dSec * 100.0) / 100.0).append(" seconds"); } LOGGER.info(msg.toString()); } catch (LockObtainFailedException e) { LOGGER.log(Level.INFO,"Optimization run aborted, reason: "+e.getMessage()); } catch (Throwable t) { LOGGER.log(Level.SEVERE,"Error optimizing index.",t); } finally { if (writer != null) { try { writer.close(); } catch (Throwable t) { LOGGER.log(Level.SEVERE,"Error closing IndexWriter.",t); } } if (backgroundLock != null) { try { backgroundLock.release(); } catch (Throwable t) { LOGGER.log(Level.WARNING,"Error releasing lock.",t); } } if (context != null) { context.onExecutionPhaseCompleted(); } if (this.wasInterrupted) { LOGGER.info("LuceneIndexOptimizer run was interrupted."); } } // optimize the assertion indexes AsnFactory asnFactory = AsnFactory.newFactory(null); AsnConfig asnConfig = asnFactory.getConfiguration(); if (asnConfig.getAreAssertionsEnabled()) { AsnIndexReferences asnIndexRefs = asnConfig.getIndexReferences(); if (asnIndexRefs != null) { for (AsnIndexReference asnIndexRef: asnIndexRefs.values()) { if ((asnIndexRef != null) && asnIndexRef.getEnabled()) { String asnLoc = asnIndexRef.getIndexLocation(); LOGGER.fine("Optimizing assertion index: "+asnLoc); try { long asnStartMillis = System.currentTimeMillis(); AsnIndexAdapter asnIndexAdapter = asnIndexRef.makeIndexAdapter(null); asnIndexAdapter.optimize(); double asnSec = (System.currentTimeMillis() - asnStartMillis) / 1000.0; StringBuffer msg = new StringBuffer(); msg.append("Optimization of assertion index complete: "+asnLoc); msg.append(", runtime: "); msg.append(Math.round(asnSec / 60.0 * 100.0) / 100.0).append(" minutes"); if (asnSec <= 600) { msg.append(", ").append(Math.round(asnSec * 100.0) / 100.0).append(" seconds"); } LOGGER.fine(msg.toString()); } catch (Exception e) { LOGGER.log(Level.SEVERE,"Error optimizing assertion index: "+asnLoc,e); } } } } } } }