/* 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.repository.HrRecord; import com.esri.gpt.framework.context.RequestContext; import com.esri.gpt.framework.resource.query.Criteria; import java.util.ArrayList; import java.util.logging.Logger; /** * Pool of working threads. */ final class Pool { /** logger */ private static final Logger LOGGER = Logger.getLogger(Pool.class.getCanonicalName()); /** data processor */ private final DataProcessor dataProcessor; /** task queue */ private final TaskQueue taskQueue; /** collection of workers*/ private ArrayList<Worker> workers = new ArrayList<Worker>(); /** collection of ad-hoc workers */ private ArrayList<OneTimeWorker> adHoc = new ArrayList<OneTimeWorker>(); /** * Creates instance of the pool. * @param dataProcessor data processor * @param taskQueue task queue to pull tasks * @param initialSize initial size of the pool */ public Pool(DataProcessor dataProcessor, TaskQueue taskQueue, int initialSize) { if (dataProcessor==null) { throw new IllegalArgumentException("No data processor provided."); } if (taskQueue==null) { throw new IllegalArgumentException("No task queue provided."); } this.dataProcessor = dataProcessor; this.taskQueue = taskQueue; this.resize(initialSize); } /** * Resizes the pool. * If shrinking, worker-thread pairs to be removed are allowed to finish their jobs. * @param size desired size of the pool */ public synchronized void resize(int size) { if (size == workers.size() || size<0) return; if (size > workers.size()) { int missing = size - workers.size(); for (int i = 0; i < missing; i++) { Worker worker = new Worker(dataProcessor, taskQueue); Thread thread = new Thread(worker, "harvester"); workers.add(worker); thread.start(); } } else { while (workers.size()>size) { Worker worker = workers.remove(workers.size() - 1); worker.end(); } } LOGGER.info("[SYNCHRONIZER] Pool size resized to: "+size()); } /** * Spans separate thread for ad hoc synchronization request. * @param resource resource to synchronize * @param criteria criteria */ public synchronized void span(HrRecord resource, Criteria criteria) { OneTimeWorker worker = new OneTimeWorker(dataProcessor, resource, criteria){ @Override protected void onComplete() { RequestContext context = RequestContext.extract(null); try { taskQueue.complete(context, resource.getUuid()); } finally { adHoc.remove(this); context.onExecutionPhaseCompleted(); } } }; adHoc.add(worker); Thread thread = new Thread(worker, "harvester ad hoc"); thread.start(); } /** * Gets pool size. * @return pool size */ public int size() { return workers.size(); } /** * Drops executing any worker thread harvesting given repository. * @param uuid repository uuid */ public boolean drop(String uuid) { boolean anythingFound = false; for (Worker worker : workers) { if (worker.isExecuting(uuid)) { worker.drop(); anythingFound = true; } } return anythingFound; } /** * Shuts down all the worker threads. */ public synchronized void shutdown() { for (Worker worker : workers) { worker.shutdown(); } for (OneTimeWorker worker : adHoc) { worker.shutdown(); } } /** * Checks if any worker is currently executing harvesting given repository. * @param record repository * @return <code>true</code> if there is a worker executing harvesting given repository */ public boolean isExecuting(String uuid) { return getExecutionUnitFor(uuid)!=null; } /** * Gets execution unit for given repository id. * @param uuid repository id * @return execution unit or <code>null</code> if execution unit not available */ public ExecutionUnit getExecutionUnitFor(String uuid) { for (Worker w : workers) { if (w.isExecuting(uuid)) { return w.getExecutionUnit(); } } for (OneTimeWorker w : adHoc) { if (w.isExecuting(uuid)) { return w.getExecutionUnit(); } } return null; } /** * Gets array of currently executed execution units. * @return array of currently executed execution units */ public ExecutionUnit [] getAllExecutedUnits() { ArrayList<ExecutionUnit> executedUnits = new ArrayList<ExecutionUnit>(); for (Worker w : workers) { ExecutionUnit unit = w.getExecutionUnit(); if (unit!=null) executedUnits.add(unit); } for (OneTimeWorker w : adHoc) { ExecutionUnit unit = w.getExecutionUnit(); if (unit!=null) executedUnits.add(unit); } return executedUnits.toArray(new ExecutionUnit[executedUnits.size()]); } public synchronized void safeSuspend() { for (WorkerBase w: workers) { w.safeSuspend(); } for (WorkerBase w: adHoc) { w.safeSuspend(); } } public synchronized void safeResume() { for (WorkerBase w: workers) { w.safeResume(); } for (WorkerBase w: adHoc) { w.safeResume(); } } }