/*==========================================================================*\ | $Id: JobQueue.java,v 1.4 2011/12/25 21:18:24 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2008-2011 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.jobqueue; import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction; import com.webobjects.eoaccess.EOGeneralAdaptorException; import com.webobjects.eoaccess.EOUtilities; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOEnterpriseObject; import com.webobjects.eocontrol.EOFetchSpecification; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import er.extensions.eof.ERXEC; import er.extensions.eof.ERXQ; import org.apache.log4j.Logger; import org.webcat.core.*; import org.webcat.core.messaging.UnexpectedExceptionMessage; import org.webcat.dbupdate.*; import org.webcat.woextensions.ECAction; import static org.webcat.woextensions.ECAction.run; import org.webcat.woextensions.WCEC; //------------------------------------------------------------------------- /** * This subsystem provides the infrastructure support for managing * database-backed job queues and worker threads for processing queued * jobs. There is comprehensive support for managing distributed and * parallel execution of worker threads, coordinated through the * corresponding queues using the central database as the mediator. * * @author Stephen Edwards * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.4 $, $Date: 2011/12/25 21:18:24 $ */ public class JobQueue extends Subsystem { //~ Constructor ........................................................... // ---------------------------------------------------------- /** * Creates a new object. */ public JobQueue() { super(); instance = this; } //~ Methods ............................................................... // ---------------------------------------------------------- public void init() { super.init(); // Mark as initialized just a bit early, so that the registration // operations in this method don't choke subsystemInitCompleted(); HostDescriptor.ensureCurrentHostIsRegistered(); log.info( "canonical host name = " + HostDescriptor.canonicalHostName()); run(new ECAction() { public void action() { try { HostDescriptor thisHost = HostDescriptor.currentHost(ec); // mark all workers for this host as not alive NSArray<WorkerDescriptor> oldWorkers = WorkerDescriptor.descriptorsForHost(ec, thisHost); for (WorkerDescriptor worker : oldWorkers) { if (worker.currentJobId() > 0L) { worker.setCurrentJobIdRaw(null); } if (worker.isAlive()) { worker.setIsAlive(false); } // mark all jobs assigned to workers on this host // as unassigned EOFetchSpecification fs = new EOFetchSpecification( worker.queue().jobEntityName(), ERXQ.is(JobBase.WORKER_KEY, worker), null); NSArray<?> jobs = ec.objectsWithFetchSpecification(fs); for (Object jobObject : jobs) { JobBase job = (JobBase)jobObject; job.setWorkerRelationship(null); } } ec.saveChanges(); } catch (Exception e) { log.error("Unexpected exception initializing subsystem", e); } }}); } // ---------------------------------------------------------- /** * Registers a descriptor in the database, if it has not already been * registered. * @param context The editing context to use. * @param descriptorEntityName The entity name for the descriptor * you want to register. * @param searchBindings a set of bindings that uniquely identify * the descriptor you are trying to register. * @param initializationBindings a set of additional bindings (that will * be added to the searchBindings) to set the initial field values * when creating the descriptor, if one does not already exist. * @param updateBindings a set of additional bindings to set if an * existing descriptor is found and is available. * @return The registered descriptor */ @SuppressWarnings("unchecked") public static EOEnterpriseObject registerFirstAvailableDescriptor( EOEditingContext context, String descriptorEntityName, NSDictionary<String, ?> searchBindings, NSDictionary<String, ?> initializationBindings, NSDictionary<String, ?> updateBindings) { ensureInitialized(); if (log.isDebugEnabled()) { log.debug("registerFirstAvailableDescriptor(" + descriptorEntityName + ", " + searchBindings + ")"); } EOEnterpriseObject result = null; try { context.lock(); NSArray descriptors = EOUtilities.objectsMatchingValues( context, descriptorEntityName, searchBindings); if (descriptors == null || descriptors.count() == 0) { try { EOEnterpriseObject descriptor = EOUtilities.createAndInsertInstance( context, descriptorEntityName); descriptor.reapplyChangesFromDictionary( (NSDictionary<String, Object>)searchBindings); log.debug("descriptor not found: creating a new one"); if (initializationBindings != null) { descriptor.reapplyChangesFromDictionary( (NSDictionary<String, Object>) initializationBindings); if (log.isDebugEnabled()) { log.debug( "initialization = " + initializationBindings); } } context.saveChanges(); } catch (EOGeneralAdaptorException e) { // Ignore it--most likely an optimistic locking failure // on the id. } } descriptors = EOUtilities.objectsMatchingValues( context, descriptorEntityName, searchBindings); if (descriptors == null || descriptors.count() == 0) { log.error("failure registering " + descriptorEntityName + searchBindings); new UnexpectedExceptionMessage(null, null, null, "failure registering " + descriptorEntityName + searchBindings).send(); } else { if (log.isDebugEnabled()) { log.debug(descriptors.count() == 1 ? "one descriptor found" : "multiple descriptors found"); } result = (EOEnterpriseObject)descriptors.objectAtIndex(0); while (descriptors.count() > 1) { result = (EOEnterpriseObject)descriptors.objectAtIndex(0); for (int i = 1; i < descriptors.count(); i++) { EOEnterpriseObject eo = (EOEnterpriseObject) descriptors.objectAtIndex(i); if (result == null) { result = eo; } else { Integer i1 = (Integer)EOUtilities .primaryKeyForObject( context, result).objectForKey( "id" ); Integer i2 = (Integer)EOUtilities .primaryKeyForObject( context, eo).objectForKey( "id" ); if (i1.intValue() > i2.intValue()) { result = eo; } } } try { result.takeValuesFromDictionary(updateBindings); log.debug("attempting to update descriptor: " + result); context.saveChanges(); } catch (EOGeneralAdaptorException e) { log.debug("delete attempt stumbled"); // Assume delete failed because the object is already // gone. context.revert(); context.refaultAllObjects(); result = null; } descriptors = EOUtilities.objectsMatchingValues( context, descriptorEntityName, searchBindings); } } } finally { context.unlock(); } if (log.isDebugEnabled()) { log.debug("registerDescriptor: result = " + result); } return result; } // ---------------------------------------------------------- /** * Registers a descriptor in the database, if it has not already been * registered. * @param context The editing context to use. * @param descriptorEntityName The entity name for the descriptor * you want to register. * @param searchBindings a set of bindings that uniquely identify * the descriptor you are trying to register. * @param initializationBindings a set of additional bindings (that will * be added to the searchBindings) to set the initial field values * when creating the descriptor, if one does not already exist. * @return The registered descriptor */ @SuppressWarnings("unchecked") public static EOEnterpriseObject registerDescriptor( EOEditingContext context, String descriptorEntityName, NSDictionary<String, ?> searchBindings, NSDictionary<String, ?> initializationBindings) { ensureInitialized(); if (log.isDebugEnabled()) { log.debug("registerDescriptor(" + descriptorEntityName + ", " + searchBindings + ")"); } EOEnterpriseObject result = null; try { context.lock(); NSArray descriptors = EOUtilities.objectsMatchingValues( context, descriptorEntityName, searchBindings); if (descriptors == null || descriptors.count() == 0) { try { EOEnterpriseObject descriptor = EOUtilities.createAndInsertInstance( context, descriptorEntityName); descriptor.reapplyChangesFromDictionary( (NSDictionary<String, Object>)searchBindings); log.debug("descriptor not found: creating a new one"); if (initializationBindings != null) { descriptor.reapplyChangesFromDictionary( (NSDictionary<String, Object>) initializationBindings); if (log.isDebugEnabled()) { log.debug( "initialization = " + initializationBindings); } } context.saveChanges(); } catch (EOGeneralAdaptorException e) { // Ignore it--most likely an optimistic locking failure // on the id. } } descriptors = EOUtilities.objectsMatchingValues( context, descriptorEntityName, searchBindings); if (descriptors == null || descriptors.count() == 0) { log.error("failure registering " + descriptorEntityName + searchBindings); new UnexpectedExceptionMessage(null, null, null, "failure registering " + descriptorEntityName + searchBindings).send(); } else { if (log.isDebugEnabled()) { log.debug(descriptors.count() == 1 ? "one descriptor found" : "multiple descriptors found"); } while (descriptors.count() > 1) { try { log.debug("attempting to delete descriptor: " + descriptors.objectAtIndex(1)); context.deleteObject( (EOEnterpriseObject)descriptors.objectAtIndex(1)); context.saveChanges(); } catch (EOGeneralAdaptorException e) { log.debug("delete attempt stumbled"); // Assume delete failed because the object is already // gone. context.revert(); context.refaultAllObjects(); } descriptors = EOUtilities.objectsMatchingValues( context, descriptorEntityName, searchBindings); } result = (EOEnterpriseObject)descriptors.objectAtIndex(0); } } finally { context.unlock(); } if (log.isDebugEnabled()) { log.debug("registerDescriptor: result = " + result); } return result; } // ---------------------------------------------------------- /** * Registers a descriptor in the database, if it has not already been * registered. * @param descriptorEntityName The entity name for the descriptor * you want to register. * @param searchBindings a set of bindings that uniquely identify * the descriptor you are trying to register. * @param initializationBindings a set of additional bindings (that will * be added to the searchBindings) to set the initial field values * when creating the descriptor, if one does not already exist. */ public static void registerDescriptor( String descriptorEntityName, NSDictionary<String, ?> searchBindings, NSDictionary<String, ?> initializationBindings) { EOEditingContext ec = WCEC.newEditingContext(); try { ec.lock(); registerDescriptor(ec, descriptorEntityName, searchBindings, initializationBindings); } finally { ec.unlock(); ec.dispose(); } } //~ Private Methods ....................................................... // ---------------------------------------------------------- private static void ensureInitialized() { if (!instance.isInitialized()) { log.error( "JobQueue subsystem has not yet been initialized " + "(check subsystem dependencies)", new Throwable("called here")); } } //~ Instance/static variables ............................................. private static JobQueue instance; static Logger log = Logger.getLogger(JobQueue.class); }