/* 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.management.MmdEnums.ApprovalStatus; 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.Val; import java.sql.SQLException; import java.util.Arrays; import java.util.Set; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; /** * Worker. A harvesting thread body. Waits for any new harvesting task to appear * in the queue and performs harvesting. */ class Worker extends WorkerBase { private static final int DEFAULT_MAX_ATTEMPTS = 1; /** * task queue */ protected final TaskQueue taskQueue; /** * dropped flag */ protected volatile boolean dropped; /** * ended flag */ protected volatile boolean ended; /** * to skip */ protected Set<String> toSkip; /** * Creates instance of the worker. * * @param dataProcessor data processor * @param taskQueue task queue */ public Worker(DataProcessor dataProcessor, TaskQueue taskQueue) { super(dataProcessor); if (taskQueue == null) { throw new IllegalArgumentException("No task queue provided."); } this.taskQueue = taskQueue; } /** * Drops harvesting current repository and move on to harvesting next one if * available, or wait for the next available. */ public synchronized void drop() { dropped = true; workerThread.interrupt(); } /** * Let's finish currently working thread, and then shut it down. Allows to * complete pending task execution, then shuts down. */ public synchronized void end() { ended = true; workerThread.interrupt(); } /** * Gets dropped flag. * * @return dropped flag */ public boolean isDropped() { return dropped; } /** * Gets ended flag. * * @return ended flag */ public boolean isEnded() { return ended; } @Override protected void execute() { int attempt = 0; do { dropped = false; try { // get next available task ExecutionUnit nextUnit = !suspended ? next() : null; // clear atempts counter attempt = 0; if (nextUnit != null) { if (ApprovalStatus.isPubliclyVisible(nextUnit.getRepository().getApprovalStatus().name()) && nextUnit.getRepository().getSynchronizable() && !isToSkip(nextUnit.getRepository().getUuid())) { // create executor and start harvesting Executor exe = newExecutor(nextUnit); setExecutor(exe); exe.execute(); } else { complete(nextUnit.getRepository().getUuid()); } } else { if (isSuspendedWithAck()) { synchronized (this) { try { wait(); } catch (InterruptedException ex) { } } } else { // wait for another task synchronized (taskQueue) { try { taskQueue.wait(60000); // wait a minute and try again } catch (InterruptedException ex) { } } } } } catch (SQLException ex) { attempt++; if (attempt<=getMaxAttempts()) { Logger.getLogger(Worker.class.getCanonicalName()).log(Level.SEVERE, "[SYNCHRONIZER] Internal worker error.", ex); } else { // wait for another task synchronized (taskQueue) { try { taskQueue.wait(60000); // wait a minute and try again } catch (InterruptedException ex1) { } } } } finally { if (executor != null && !shutdown) { ExecutionUnit unit = executor.getExecutionUnit(); if (unit != null) { complete(unit.getRepository().getUuid()); } } setExecutor(null); } } while (!shutdown && !ended); } /** * Creates new executor. * * @param unit execution unit * @return executor */ private Executor newExecutor(ExecutionUnit unit) { return unit.getRepository().getProtocol().newExecutor(dataProcessor, unit, this); } /** * Completes task. * * @param uuid repository uuid */ private void complete(String uuid) { RequestContext context = RequestContext.extract(null); try { taskQueue.complete(context, uuid); } finally { context.onExecutionPhaseCompleted(); } } /** * Gets execution nextUnit for the next task. * * @return execution nextUnit * @throws SQLException if accessing database fails */ private ExecutionUnit next() throws SQLException { RequestContext context = RequestContext.extract(null); try { final Task task = taskQueue.next(context); if (task == null) { return null; } return newExecutionUnit(task); } finally { context.onExecutionPhaseCompleted(); } } protected boolean isToSkip(String uuid) { if (toSkip == null) { toSkip = getSitesToSkip(); } return toSkip != null ? toSkip.contains(uuid) : false; } protected Set<String> getSitesToSkip() { ApplicationContext appCtx = ApplicationContext.getInstance(); ApplicationConfiguration appCfg = appCtx.getConfiguration(); StringAttributeMap parameters = appCfg.getCatalogConfiguration().getParameters(); String skip = Val.chkStr(parameters.getValue("webharvester.skip")); TreeSet<String> set = new TreeSet<String>(); set.addAll(Arrays.asList(skip.split(","))); return set; } @Override public void safeResume() { synchronized (taskQueue) { super.safeResume(); taskQueue.notifyAll(); } } @Override public void safeSuspend() { synchronized (taskQueue) { super.safeSuspend(); taskQueue.notifyAll(); } } @Override public boolean isActive() { return super.isActive() && !isDropped(); } private boolean isSuspendedWithAck() { if (isSuspended()) { Logger.getLogger(Worker.class.getCanonicalName()).log(Level.INFO, "[SYNCHRONIZER] Worker {0} acknowledged suspension", workerThread.getId()); } return isSuspended(); } 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); } }