/* 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.harvest.jobs.HjLoadAllRequest; import com.esri.gpt.catalog.harvest.jobs.HjRecord; import com.esri.gpt.catalog.harvest.jobs.HjRecord.JobStatus; import com.esri.gpt.catalog.harvest.jobs.HjRecords; import com.esri.gpt.catalog.harvest.jobs.HjWithdrawRequest; import com.esri.gpt.framework.collection.StringAttributeMap; import com.esri.gpt.framework.context.ApplicationConfiguration; import com.esri.gpt.framework.context.ApplicationContext; import com.esri.gpt.framework.context.RequestContext; import com.esri.gpt.framework.util.TimePeriod; import com.esri.gpt.framework.util.Val; import java.sql.SQLException; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; /** * Watch-dog. * Checks every specified period of time for any panding jobs which has been * canceled by another instance of the application. If any of such a jobs are * found, a real drop of worker is performed on that machine, which is actually * executing a task. */ abstract class WatchDog implements Runnable { private static final int DEFAULT_MAX_ATTEMPTS = 1; /** logger */ private static final Logger LOGGER = Logger.getLogger(WatchDog.class.getCanonicalName()); /** current thread */ private Thread workerThread; /** shutdown flag */ private volatile boolean shutdown; /** autoselect frequency */ private long watchDogFrequency; /** suspended */ private volatile boolean suspended; /** * Creates instance of the watch-dog. * @param watchDogFrequency watch-dog frequency (milliseconds) */ public WatchDog(long watchDogFrequency) { this.watchDogFrequency = watchDogFrequency; } @Override public void run() { workerThread = Thread.currentThread(); LOGGER.info("[SYNCHRONIZER] Watch-dog activated."); int attempt = 0; do { if (!suspended) { LOGGER.finer("[SYNCHRONIZER] Watch-dog entered run mode."); String[] uuids = getCurrentlyHarvesterResourceUuids(); String[] canceledUuids = new String[]{}; try { ArrayList<String> uuidsToCancel = new ArrayList<String>(); HjRecords records = selectAll(uuids); for (HjRecord r : records) { if (r.getStatus() == JobStatus.Canceled) { uuidsToCancel.add(r.getHarvestSite().getUuid()); } } canceledUuids = uuidsToCancel.toArray(new String[uuidsToCancel.size()]); if (uuidsToCancel.size() > 0) { LOGGER.finer("[SYNCHRONIZER] Watch-dog loaded tasks to drop for resources: " + uuidsToCancel.toString()); } else { LOGGER.finer("[SYNCHRONIZER] Watch-dog loaded no tasks to drop."); } cancelByResourceUuids(canceledUuids); if (canceledUuids.length > 0) { withdrawAll(canceledUuids); } // clear attempt counter attempt = 0; } catch (SQLException ex) { attempt++; if (attempt<=getMaxAttempts()) { LOGGER.log(Level.SEVERE, "[SYNCHRONIZER] Error loading tasks for Watch-dog.", ex); } } } if (shutdown) break; // wait for calculated duration or until interrupted synchronized (this) { try { if (isSuspendedWithAck()) { LOGGER.finer("[SYNCHRONIZER] Watch-dog suspended mode"); wait(); } else { TimePeriod period = new TimePeriod(); period.setValue(watchDogFrequency); LOGGER.finer("[SYNCHRONIZER] Watch-dog enters wait mode for " + period); wait(watchDogFrequency); } } catch (InterruptedException ex) { if (shutdown) break; } } } while (true); } /** * Shuts down selector. */ public synchronized void shutdown() { LOGGER.info("[SYNCHRONIZER] Shutting down Watch-dog."); this.shutdown = true; if (workerThread != null) { workerThread.interrupt(); } } /** * Gets array of currently beiing harvested resources uuids. * @return array of currently beiing harvested resources uuids */ protected abstract String[] getCurrentlyHarvesterResourceUuids(); /** * Cancels all tasks specified by resource uuid * @param uuids array of resource uuids */ protected abstract void cancelByResourceUuids(String[] uuids); /** * Selects all records * @param uuids uuids of the records * @return collection of records * @throws SQLException if accessing database fails */ private HjRecords selectAll(String[] uuids) throws SQLException { RequestContext context = RequestContext.extract(null); try { HjLoadAllRequest loadAllRequest = new HjLoadAllRequest(context, uuids); loadAllRequest.execute(); return loadAllRequest.getQueryResult().getRecords(); } finally { context.onExecutionPhaseCompleted(); } } /** * Withdraws all records. * @param uuids uuids of the records to withdraw * @throws SQLException if accessing database fails */ private void withdrawAll(String[] uuids) throws SQLException { RequestContext context = RequestContext.extract(null); try { HjWithdrawRequest withdrawRequest = new HjWithdrawRequest(context, uuids); withdrawRequest.execute(); } finally { context.onExecutionPhaseCompleted(); } } public synchronized void safeSuspend() { if (!suspended) { LOGGER.info("[SYNCHRONIZER] Suspending Watch-Dog"); suspended = true; notify(); } else { LOGGER.info("[SYNCHRONIZER] Watch-Dog already suspended"); } } public synchronized void safeResume() { if (suspended) { LOGGER.info("[SYNCHRONIZER] Resuming Watch-Dog"); suspended = false; notify(); } else { LOGGER.info("[SYNCHRONIZER] Watch-Dog already resumed"); } } private boolean isSuspendedWithAck() { if (suspended) { LOGGER.info("[SYNCHRONIZER] Watch-Dog acknowledged suspension"); } return suspended; } private int getMaxAttempts() { ApplicationContext appCtx = ApplicationContext.getInstance(); ApplicationConfiguration appCfg = appCtx.getConfiguration(); StringAttributeMap parameters = appCfg.getCatalogConfiguration().getParameters(); return Val.chkInt(parameters.getValue("webharvester.maxAttempts"),DEFAULT_MAX_ATTEMPTS); } }