/* 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.control.webharvest.engine; import com.esri.gpt.catalog.lucene.LuceneIndexSynchronizer; import com.esri.gpt.framework.collection.StringAttributeMap; import com.esri.gpt.framework.context.ApplicationContext; import com.esri.gpt.framework.context.RequestContext; import com.esri.gpt.framework.http.HttpClientRequest; import com.esri.gpt.framework.scheduler.IScheduledTask; import com.esri.gpt.framework.util.TimePeriod; import com.esri.gpt.framework.util.Val; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.logging.Level; import java.util.logging.Logger; /** * Performs a scheduled pause of the harvesting engine. * <p> * Intended to be used when multiple remote nodes are synchronizing * a local Lucene index at a scheduled time. * </p> */ public class ScheduledPause implements Runnable, IScheduledTask { private final static long DEFAULT_CONNECTION_TIMEOUT = 30 * 60 * 1000; // 30 minutes private final static long DEFAULT_RESPONSE_TIMEOUT = 30 * 60 * 1000; // 30 minutes private final static long DEFAULT_INITIAL_SLEEP_TIME = 30 * 60 * 1000; // 30 minutes private final static long DEFAULT_CONSECUTIVE_SLEEP_TIME = 15 * 60 * 1000; // 15 minutes /** class variables ========================================================= */ private static Logger LOGGER = Logger.getLogger(ScheduledPause.class.getName()); /** instance variables ====================================================== */ private StringAttributeMap parameters = null; private boolean wasInterrupted = false; /** constructors =========================================================== */ /** Default constructor. */ public ScheduledPause() {} /** 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; } /** * Check to see if remote nodes with local Lucene indexes are * actively running the Lucene index synchronizer. * @param taskParams the scheduled task parameters */ private RemoteIndexerInfo checkRemoteIndexers(StringAttributeMap taskParams) { long tStartMillis = System.currentTimeMillis(); String sUrls = Val.chkStr(taskParams.getValue("remoteIndexingUrls")); LOGGER.info("Checking remoteIndexingUrls: "+sUrls); String[] aUrls = Val.tokenize(sUrls,","); RemoteIndexerInfo info = new RemoteIndexerInfo(); info.numUrls = aUrls.length; String connectionTimeout = taskParams.getValue("connectionTimeout"); String responseTimeout = taskParams.getValue("responseTimeout"); try { for (String sUrl: aUrls) { if ("self".equalsIgnoreCase(sUrl)) { boolean isRunning = LuceneIndexSynchronizer.RUNNING; if (isRunning) { info.numActive++; } else { info.numInactive++; } } else { sUrl += "?action=isSynchronizerRunning"; HttpClientRequest request = new HttpClientRequest(); request.setConnectionTimeMs((int)parsePeriod(connectionTimeout, DEFAULT_CONNECTION_TIMEOUT).getValue()); request.setResponseTimeOutMs((int)parsePeriod(responseTimeout, DEFAULT_RESPONSE_TIMEOUT).getValue()); request.setUrl(sUrl); try { LOGGER.info("Checking: "+sUrl); String response = Val.chkStr(request.readResponseAsCharacters()); LOGGER.info("Response from: "+sUrl+" ="+response); if (response.equalsIgnoreCase("true")) { info.numActive++; } else if (response.equalsIgnoreCase("false")) { info.numInactive++; } else { info.numUnexpected++; } } catch (SocketTimeoutException ex) { info.numTimedOut++; LOGGER.info("Timeout on: "+sUrl); } catch (IOException e) { info.numFailed++; LOGGER.log(Level.SEVERE,"Error with: "+sUrl,e); } } } } finally { double dSec = (System.currentTimeMillis() - tStartMillis) / 1000.0; StringBuilder msg = new StringBuilder(); msg.append(" Check remote indexers:"); msg.append(" urls=").append(aUrls.length); msg.append(", active=").append(info.numActive); msg.append(", inactive=").append(info.numInactive); msg.append(", unexpected=").append(info.numUnexpected); msg.append(", timedout=").append(info.numTimedOut); msg.append(", failed=").append(info.numFailed); msg.append(", time=").append(dSec).append("seconds"); LOGGER.info(msg.toString()); } return info; } /** * Run the process. */ @Override public void run() { LOGGER.info("Harvester scheduled pause run started..."); RequestContext context = null; String initialSleepTime = parameters.getValue("initialSleepTime"); String consecutiveSleepTime = parameters.getValue("consecutiveSleepTime"); long tInitialTime = parsePeriod(initialSleepTime, DEFAULT_INITIAL_SLEEP_TIME).getValue(); long tConsecutiveTime = parsePeriod(consecutiveSleepTime, DEFAULT_CONSECUTIVE_SLEEP_TIME).getValue(); // suspend harvesting engine getHarvestingEngine().safeSuspend(); try { // initial sleep Thread.sleep(tInitialTime); do { RemoteIndexerInfo info = checkRemoteIndexers(this.parameters); if (checkInterrupted()) return; if (info.numActive==0) break; // consecutive sleep Thread.sleep(tConsecutiveTime); } while (true); } catch (Throwable t) { LOGGER.log(Level.SEVERE,"Error during scheduled pause.",t); } finally { // we are done waiting, resume harvesting if (!checkInterrupted()) { LOGGER.info("Harvester scheduled pause completed, resuming harvester..."); getHarvestingEngine().safeResume(); } if (context != null) { context.onExecutionPhaseCompleted(); } if (this.wasInterrupted) { LOGGER.info("Harvester scheduled pause was interrupted."); } } } /** * Safely parses time period giving default value if time period can not be parsed. * @param periodDef period definition to parse * @param defaultValue default value if period definition cannot be parsed * @return time period */ private TimePeriod parsePeriod(String periodDef, long defaultValue) { try { return TimePeriod.parseValue(periodDef); } catch (IllegalArgumentException ex) { return new TimePeriod(defaultValue); } } private Harvester getHarvestingEngine() { return ApplicationContext.getInstance().getHarvestingEngine(); } /** Stores information collected checking remote indexers. */ class RemoteIndexerInfo { int numUrls = 0; int numActive = 0; int numInactive = 0; int numUnexpected = 0; int numFailed = 0; int numTimedOut = 0; boolean isOk() { boolean bOk = ((numActive + numInactive + numTimedOut) == numUrls); if (!bOk) { bOk = ((numActive + numInactive + numTimedOut + numUnexpected) == numUrls); } return bOk; } } }