| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| ContactManager |
|
| 6.125;6.125 |
| 1 | /****************************************************************************** | |
| 2 | * ContactManager.java - Manage making the contact sheets. Insure single threadedness | |
| 3 | * (two copies could lead to _heavens_ out of memory | |
| 4 | * | |
| 5 | * PicMan - The BuckoSoft Picture Manager in Java | |
| 6 | * Copyright(c) 2005 - Dick Balaska | |
| 7 | * | |
| 8 | */ | |
| 9 | package com.buckosoft.PicMan.business; | |
| 10 | ||
| 11 | import java.io.File; | |
| 12 | import java.util.ArrayList; | |
| 13 | import java.util.Date; | |
| 14 | import java.util.HashMap; | |
| 15 | import java.util.Iterator; | |
| 16 | import java.util.LinkedList; | |
| 17 | import java.util.List; | |
| 18 | import java.util.concurrent.locks.ReentrantLock; | |
| 19 | ||
| 20 | import org.apache.commons.logging.Log; | |
| 21 | import org.apache.commons.logging.LogFactory; | |
| 22 | ||
| 23 | import com.buckosoft.PicMan.business.contact.ContactEngine; | |
| 24 | import com.buckosoft.PicMan.domain.Chain; | |
| 25 | import com.buckosoft.PicMan.domain.ContactParams; | |
| 26 | import com.buckosoft.PicMan.domain.Filter; | |
| 27 | import com.buckosoft.PicMan.domain.FilterMicroSet; | |
| 28 | import com.buckosoft.PicMan.domain.JobLogEntry; | |
| 29 | import com.buckosoft.PicMan.domain.MetaSet; | |
| 30 | import com.buckosoft.PicMan.domain.MetaSetRule; | |
| 31 | import com.buckosoft.PicMan.domain.Pic; | |
| 32 | import com.buckosoft.PicMan.domain.PosterParams; | |
| 33 | import com.buckosoft.PicMan.domain.Set; | |
| 34 | import com.buckosoft.PicMan.domain.SetSize; | |
| 35 | import com.buckosoft.PicMan.domain.System; | |
| 36 | ||
| 37 | /** | |
| 38 | * Contact Manager. Decide when and what contact sheets to make. | |
| 39 | * Singleton ContactManager gets invoked | |
| 40 | * @author Dick Balaska | |
| 41 | * @since 2005/08/06 | |
| 42 | * @see <a href="http://cvs.buckosoft.com/Projects/PicMan/PicMan/src/main/java/com/buckosoft/PicMan/business/ContactManager.java">ContactManager.java</a> | |
| 43 | */ | |
| 44 | 0 | public class ContactManager implements SetChangedListener { |
| 45 | ||
| 46 | 0 | private static boolean DEBUG = false; |
| 47 | private static final boolean DEBUGENTRY = false; | |
| 48 | private static final boolean DEBUGSPIN = false; | |
| 49 | ||
| 50 | 0 | protected final Log logger = LogFactory.getLog(getClass()); |
| 51 | ||
| 52 | // Debugging tools (manipulate the run) | |
| 53 | // private static final boolean runNone = false; | |
| 54 | // private static final boolean runOnce = false; | |
| 55 | private static final boolean onePicOnly = false; | |
| 56 | ||
| 57 | private PicManFacade pmf; | |
| 58 | ||
| 59 | 0 | private LinkedList<ContactParams> contactList = new LinkedList<ContactParams>(); |
| 60 | 0 | private LinkedList<ContactParams> spinList = new LinkedList<ContactParams>(); |
| 61 | private String contactRunning; | |
| 62 | 0 | private boolean abortRequest = false; |
| 63 | ||
| 64 | private List<String> cachedPicNames; | |
| 65 | private ContactParams cachedContactParams; | |
| 66 | ||
| 67 | 0 | private ReentrantLock cpLock = new ReentrantLock(); |
| 68 | private ContactParams currentContactParams; | |
| 69 | ||
| 70 | public void setPicMan(PicManFacade pmf) { | |
| 71 | 0 | this.pmf = pmf; |
| 72 | 0 | pmf.addSetChangedListener(this); |
| 73 | 0 | } |
| 74 | ||
| 75 | /** | |
| 76 | * @return the contactRunning | |
| 77 | */ | |
| 78 | public String getContactRunning() { | |
| 79 | 0 | return contactRunning; |
| 80 | } | |
| 81 | ||
| 82 | public boolean makeContacts() throws Exception { | |
| 83 | try { | |
| 84 | 0 | contactList = determineContactsToMake(); |
| 85 | 0 | } catch (Exception e) { |
| 86 | // exitRun(); | |
| 87 | 0 | throw e; |
| 88 | 0 | } |
| 89 | 0 | boolean workDone = false; |
| 90 | if (DEBUGENTRY) | |
| 91 | logger.info("process contacts: " + contactList.size()); | |
| 92 | 0 | Iterator<ContactParams> it = contactList.iterator(); |
| 93 | 0 | if (it.hasNext()) { |
| 94 | // contactMaker = new ContactMaker(); | |
| 95 | // contactMaker.setPicMan(pmf); | |
| 96 | // contactMaker.setContactManager(this); | |
| 97 | 0 | workDone = true; |
| 98 | } | |
| 99 | try { | |
| 100 | 0 | JobLogEntry jle = null; |
| 101 | 0 | while (it.hasNext()) { |
| 102 | 0 | ContactParams cp = (ContactParams)it.next(); |
| 103 | 0 | setContactParamsRunning(cp); |
| 104 | 0 | contactRunning = cp.getUuid(); |
| 105 | ||
| 106 | 0 | if (!pmf.getDB().getSystem().isJobLogSummaryOnly() || cp.getContactNumber() == 0) { |
| 107 | 0 | if (DEBUG) |
| 108 | 0 | logger.debug("Start log entry for " + cp.getContactNumber()); |
| 109 | 0 | jle = new JobLogEntry(); |
| 110 | //jle.setChainId(cp.getCid()); | |
| 111 | 0 | jle.setNote(pmf.getDB().getChain(cp.getCid()).getName()); |
| 112 | 0 | if (!pmf.getDB().getSystem().isJobLogSummaryOnly()) |
| 113 | 0 | jle.setName(cp.getUuid()); |
| 114 | else | |
| 115 | 0 | jle.setName(cp.getUid()); |
| 116 | 0 | jle.setType(JobLogEntry.CONTACT); |
| 117 | 0 | pmf.addJobToLog(jle); |
| 118 | } | |
| 119 | 0 | ContactEngine contactEngine = null; |
| 120 | 0 | String engineName = cp.getEngineName(); |
| 121 | 0 | if (cp instanceof PosterParams) { |
| 122 | try { | |
| 123 | 0 | contactEngine = (ContactEngine)Class.forName("com.buckosoft.PicMan.business.contact.PosterEngine").newInstance(); |
| 124 | 0 | } catch (Exception e) { |
| 125 | 0 | Exception ex = new Exception("Can't instantiate Poster Engine: ", e); |
| 126 | 0 | pmf.addError(ex); |
| 127 | 0 | } |
| 128 | ||
| 129 | } | |
| 130 | try { | |
| 131 | 0 | if (contactEngine == null) |
| 132 | 0 | contactEngine = (ContactEngine)Class.forName("com.buckosoft.PicMan.business.contact.engine." + engineName).newInstance(); |
| 133 | 0 | } catch (Exception e) { |
| 134 | 0 | Exception ex = new Exception("Can't instantiate Contact Engine: " + engineName, e); |
| 135 | 0 | pmf.addError(ex); |
| 136 | 0 | } |
| 137 | 0 | boolean ret = false; |
| 138 | 0 | if (contactEngine != null) { |
| 139 | 0 | contactEngine.setPicMan(pmf); |
| 140 | 0 | contactEngine.setContactManager(this); |
| 141 | 0 | ret = contactEngine.makeContact(cp); |
| 142 | } | |
| 143 | 0 | if (isAbort()) |
| 144 | 0 | jle.setNote("*" + jle.getNote()); |
| 145 | 0 | if (ret == false) { |
| 146 | 0 | jle.setError(); |
| 147 | 0 | jle.setEndTime(); |
| 148 | 0 | pmf.addJobToLog(jle); |
| 149 | } | |
| 150 | 0 | if (!pmf.getDB().getSystem().isJobLogSummaryOnly() |
| 151 | 0 | || cp.getContactNumber() >= pmf.getDB().getChain(cp.getCid()).getContactCount()-1 |
| 152 | || cp instanceof PosterParams | |
| 153 | 0 | || isAbort()) { // turned off by user |
| 154 | 0 | if (DEBUG) |
| 155 | 0 | logger.debug("Finish log entry for " + cp.getContactNumber()); |
| 156 | 0 | jle.setEndTime(); |
| 157 | 0 | pmf.addJobToLog(jle); |
| 158 | } | |
| 159 | 0 | if (isAbort()) { |
| 160 | 0 | clearAbort(); |
| 161 | 0 | setContactParamsRunning(null); |
| 162 | 0 | break; |
| 163 | } | |
| 164 | 0 | removeFromSpin(cp); |
| 165 | 0 | } |
| 166 | 0 | } catch (Exception e) { |
| 167 | //exitRun(); | |
| 168 | 0 | throw e; |
| 169 | 0 | } |
| 170 | 0 | return(workDone); |
| 171 | } | |
| 172 | ||
| 173 | ||
| 174 | private void setContactParamsRunning(ContactParams cp) { | |
| 175 | 0 | cpLock.lock(); |
| 176 | try { | |
| 177 | 0 | currentContactParams = cp; |
| 178 | } finally { | |
| 179 | 0 | cpLock.unlock(); |
| 180 | 0 | } |
| 181 | 0 | } |
| 182 | ||
| 183 | public ContactParams getContactParamsRunning() { | |
| 184 | ContactParams cp; | |
| 185 | 0 | cpLock.lock(); |
| 186 | try { | |
| 187 | 0 | cp = currentContactParams; |
| 188 | } finally { | |
| 189 | 0 | cpLock.unlock(); |
| 190 | 0 | } |
| 191 | 0 | return(cp); |
| 192 | } | |
| 193 | /** Should we abort the current operation? | |
| 194 | * First check if the engines were turned off. | |
| 195 | * Then check if there is an abortRequest. If there is, clear it and return true. | |
| 196 | * @return Are we aborting? | |
| 197 | */ | |
| 198 | public boolean isAbort() { | |
| 199 | 0 | if (!pmf.getDB().getSystem().isEngineOn()) |
| 200 | 0 | return(true); |
| 201 | 0 | if (!this.abortRequest) |
| 202 | 0 | return(false); |
| 203 | 0 | return(true); |
| 204 | } | |
| 205 | ||
| 206 | private void clearAbort() { | |
| 207 | 0 | this.abortRequest = false; |
| 208 | 0 | } |
| 209 | ||
| 210 | /** If we happen to be building contacts for the set that changed, then kill it, because it | |
| 211 | * has to be started over anyway. | |
| 212 | * @see com.buckosoft.PicMan.business.SetChangedListener#onSetChanged(com.buckosoft.PicMan.domain.SetSize) | |
| 213 | */ | |
| 214 | @Override | |
| 215 | public void onSetChanged(SetSize ss) { | |
| 216 | 0 | cpLock.lock(); |
| 217 | try { | |
| 218 | 0 | if (currentContactParams != null |
| 219 | 0 | && ss.getSize() == currentContactParams.getSize() |
| 220 | 0 | && ss.getSetName().equals(currentContactParams.getSetName())) { |
| 221 | 0 | this.abortRequest = true; |
| 222 | 0 | logger.info("onSetChanged: aborting build of " + ss.getSetSize()); |
| 223 | } | |
| 224 | } finally { | |
| 225 | 0 | cpLock.unlock(); |
| 226 | 0 | } |
| 227 | 0 | } |
| 228 | ||
| 229 | ||
| 230 | /** Build a list of Contacts that need to be run | |
| 231 | * | |
| 232 | * @return the list | |
| 233 | */ | |
| 234 | public LinkedList<ContactParams> determineContactsToMake() { | |
| 235 | 0 | LinkedList<ContactParams> theList = new LinkedList<ContactParams>(); |
| 236 | 0 | theList.clear(); |
| 237 | 0 | List<Chain> chains = pmf.getDB().getChains(); |
| 238 | // List sets = pmf.getSets(); | |
| 239 | // List sizes = pmf.getSizes(); | |
| 240 | // Iterator it = sets.iterator(); | |
| 241 | 0 | HashMap<String, Date> filterAgeMap = pmf.getDB().getSetTimestamps(); |
| 242 | 0 | Iterator<Chain> iter = chains.iterator(); |
| 243 | 0 | while (iter.hasNext()) { |
| 244 | 0 | Chain chain = (Chain)iter.next(); |
| 245 | 0 | if (!chain.isActive()) |
| 246 | 0 | continue; |
| 247 | 0 | int contactsPerSet = chain.getContactCount(); |
| 248 | 0 | HashMap<String, Date> contactAgeMap = pmf.getDB().getContactOldestMap(chain); |
| 249 | // Set set = (Set)it.next(); | |
| 250 | // String cSet = set.getName(); | |
| 251 | // Iterator is = sizes.iterator(); | |
| 252 | 0 | Iterator<SetSize> iss = chain.getSetSizes().iterator(); |
| 253 | 0 | while (iss.hasNext()) { |
| 254 | 0 | SetSize ss = (SetSize)iss.next(); |
| 255 | //int size = ((Integer)is.next()).intValue(); | |
| 256 | ||
| 257 | 0 | if (!isSpin(chain.getCid(), ss.getSetName(), ss.getSize(), -1)) { // isSpin doesn't do the tests and goes straight to build |
| 258 | 0 | Date fdate = (Date)filterAgeMap.get(ss.getSetSize()); |
| 259 | 0 | Date cdate = (Date)contactAgeMap.get(ss.getGuiSetSize()); |
| 260 | 0 | if (DEBUG) |
| 261 | 0 | logger.debug("Comparing: " + System.getUuid(ss.getSetName(), ss.getSize()) + " f:" + fdate + " c: " + cdate); |
| 262 | 0 | Date fdateWild = (Date)filterAgeMap.get(ss.getSetName()); |
| 263 | 0 | if (fdateWild != null) { |
| 264 | 0 | if (cdate != null) { |
| 265 | 0 | if (fdateWild.after(cdate)) { // we have a wild card match. |
| 266 | 0 | fdate = null; // null out fdate so we fall through and build |
| 267 | 0 | cdate = null; |
| 268 | } | |
| 269 | } | |
| 270 | } | |
| 271 | 0 | if (fdate != null) { |
| 272 | 0 | if (cdate != null) { |
| 273 | 0 | if (DEBUG) |
| 274 | 0 | logger.debug("class: f=" + fdate.getClass() + " c=" + cdate.getClass()); |
| 275 | 0 | if (fdate.before(cdate)) |
| 276 | 0 | continue; |
| 277 | } | |
| 278 | } else { // no filter date, assume old | |
| 279 | 0 | if (cdate != null) // if there's a contact |
| 280 | 0 | continue; // then don't rebuild it |
| 281 | } | |
| 282 | 0 | if (DEBUG) |
| 283 | 0 | logger.debug("Building set: " + ss.getSetSize()); |
| 284 | } | |
| 285 | 0 | for (int i=0; i<contactsPerSet; i++) { |
| 286 | 0 | ContactParams cp = new ContactParams(); |
| 287 | 0 | cp.setContactNumber(i); |
| 288 | 0 | cp.setCid(chain.getCid()); |
| 289 | 0 | cp.setSetSize(ss); |
| 290 | 0 | cp.setRankMinMax(chain.getRatingMin(), chain.getRatingMax()); |
| 291 | 0 | cp.setEngineName(chain.getEngine()); |
| 292 | if (onePicOnly) { | |
| 293 | cp.setSize(300); | |
| 294 | cp.setSetName("X"); | |
| 295 | } | |
| 296 | 0 | File dir = new File(chain.getContactDirectory() + File.separator + ss.getSetName()); |
| 297 | //logger.debug("try dir '" + dir.getPath() + "'"); | |
| 298 | try { | |
| 299 | 0 | if (!dir.isDirectory()) |
| 300 | 0 | dir = dir.getParentFile(); |
| 301 | 0 | } catch (Exception e) { |
| 302 | 0 | dir = dir.getParentFile(); |
| 303 | 0 | } |
| 304 | //logger.debug("got dir '" + dir.getPath() + "'"); | |
| 305 | 0 | cp.setOutputDirectory(dir.getPath()); |
| 306 | 0 | theList.add(cp); |
| 307 | if (onePicOnly) | |
| 308 | return(theList); | |
| 309 | } | |
| 310 | 0 | } |
| 311 | 0 | } |
| 312 | 0 | for (ContactParams cp : spinList) { |
| 313 | 0 | if (cp instanceof PosterParams) |
| 314 | 0 | theList.add(cp); |
| 315 | 0 | } |
| 316 | 0 | return(theList); |
| 317 | } | |
| 318 | ||
| 319 | public List<String> getPicNames(ContactParams cp) { | |
| 320 | 0 | if (cachedContactParams != null) { |
| 321 | 0 | if (cp.equalsPics(cachedContactParams)) { |
| 322 | 0 | ArrayList<String> al = new ArrayList<String>(cachedPicNames); |
| 323 | 0 | return(al); |
| 324 | } | |
| 325 | } | |
| 326 | 0 | cachedPicNames = pmf.getDB().getPicNamesBySet(cp.getSetName(), cp.getSize()); |
| 327 | // cachedPicNames = setupPicsList(cp, cachedPicNames); // XXX: Is this neccessary? It slowly prunes an already pruned list. | |
| 328 | 0 | cachedContactParams = cp; |
| 329 | 0 | ArrayList<String> al = new ArrayList<String>(cachedPicNames); |
| 330 | 0 | return(al); |
| 331 | } | |
| 332 | ||
| 333 | protected List<String> setupPicsList(ContactParams cp, List<String> picNames) { | |
| 334 | // if (cp.getType() == ContactParams.TYPE_SEQUENTIAL) | |
| 335 | // return(f); | |
| 336 | 0 | ArrayList<String> al = new ArrayList<String>(picNames); |
| 337 | 0 | Iterator<String> iter = al.iterator(); |
| 338 | 0 | while (iter.hasNext()) { |
| 339 | 0 | String picName = iter.next(); |
| 340 | 0 | if (isFilteredOut(cp, picName)) |
| 341 | 0 | iter.remove(); |
| 342 | 0 | } |
| 343 | 0 | return(al); |
| 344 | } | |
| 345 | protected boolean isFilteredOut(ContactParams cp, String picName) { | |
| 346 | 0 | Filter f = pmf.getDB().getFilter(picName); |
| 347 | 0 | double rating = 9; |
| 348 | 0 | Set set = pmf.getDB().getSet(cp.getSetName()); |
| 349 | 0 | if (set.isMetaSet()) { |
| 350 | // XXX: THIS NEEDS FIXING | |
| 351 | // XXX: THIS NEEDS FIXING | |
| 352 | // XXX: THIS NEEDS FIXING | |
| 353 | /* XXX: Still needs fixing. There are actually two types (sigh) of MetaSet. MD is M+D/2. mlbXall is G|M|PG|R|XO|XX. Any 7 should pass the large test. | |
| 354 | * Ah. The difference is as stated. M&D is different than G|M|PG|R|XO|XX | |
| 355 | */ | |
| 356 | 0 | rating = pmf.getDB().getPicRate(picName, set, cp.getSize()); |
| 357 | 0 | boolean nono = false; |
| 358 | 0 | if (nono) { |
| 359 | 0 | MetaSet ms = pmf.getDB().getMetaSet(set.getSid()); |
| 360 | 0 | List<MetaSetRule> msrList = ms.getRules(); |
| 361 | 0 | Iterator<MetaSetRule> msrIter = msrList.iterator(); |
| 362 | 0 | int count = 0; |
| 363 | 0 | while (msrIter.hasNext()) { |
| 364 | //MetaSetRule msr = msrIter.next(); | |
| 365 | //if (msr.) | |
| 366 | 0 | rating *= count; |
| 367 | 0 | int prate = f.getFilter(cp.getSetName(), cp.getSize()); |
| 368 | 0 | rating += prate; |
| 369 | 0 | count++; |
| 370 | 0 | rating /= count; |
| 371 | 0 | } |
| 372 | } | |
| 373 | 0 | } else if (set.isMicroSet() || set.isNanoSet()) { |
| 374 | 0 | Pic pic = pmf.getDB().getPic(picName); |
| 375 | 0 | FilterMicroSet fms = pmf.getDB().getPicFromFilterMicroSet(pic.getPid(), set.getSid()); |
| 376 | 0 | if (fms != null) |
| 377 | 0 | rating = fms.getValue(); |
| 378 | else | |
| 379 | 0 | rating = 0; |
| 380 | 0 | } else { |
| 381 | 0 | rating = f.getFilter(cp.getSetName(), cp.getSize()); |
| 382 | } | |
| 383 | 0 | logger.debug("isFilteredOut: " + picName + " " + rating); |
| 384 | 0 | if (rating < cp.getRankMin()) |
| 385 | 0 | return(true); |
| 386 | 0 | if (cp.getRankMax() == 9) // passes the min test, no max. (hack to allow obsolete 10s from the database) |
| 387 | 0 | return(false); |
| 388 | 0 | if (rating > cp.getRankMax()) |
| 389 | 0 | return(true); |
| 390 | 0 | return(false); |
| 391 | } | |
| 392 | ||
| 393 | ||
| 394 | /** Add a list to the contacts to spin (remake) | |
| 395 | * @param list A List of ContactParams to remake | |
| 396 | */ | |
| 397 | ||
| 398 | public void addSpin(List<ContactParams> list) { | |
| 399 | 0 | Iterator<ContactParams> iter = list.iterator(); |
| 400 | 0 | while (iter.hasNext()) { |
| 401 | 0 | ContactParams cp = iter.next(); |
| 402 | if (DEBUGSPIN) | |
| 403 | logger.info("addSpin: cp cid=" + cp.getCid() + " ss= " + cp.getSetName() + "_" + cp.getSize() + " cn=" + cp.getContactNumber()); | |
| 404 | 0 | if (!isOnList(spinList, cp)) { |
| 405 | 0 | spinList.add(cp); |
| 406 | } | |
| 407 | 0 | } |
| 408 | 0 | } |
| 409 | ||
| 410 | private boolean isOnList(List<ContactParams> list, ContactParams cp) { | |
| 411 | 0 | Iterator<ContactParams> iter = list.iterator(); |
| 412 | 0 | while (iter.hasNext()) { |
| 413 | 0 | ContactParams cpl = iter.next(); |
| 414 | 0 | if (cpl.equals(cp)) |
| 415 | 0 | return(true); |
| 416 | 0 | } |
| 417 | 0 | return(false); |
| 418 | } | |
| 419 | /** Is this contact specififed on the spin list? | |
| 420 | * @param cid The chainId | |
| 421 | * @param setName The setName | |
| 422 | * @param size The size | |
| 423 | * @param contactNumber The contact sequence number | |
| 424 | * @return true if this is a user specified spin request (on the spin list) | |
| 425 | */ | |
| 426 | private boolean isSpin(int cid, String setName, int size, int contactNumber) { | |
| 427 | 0 | Iterator<ContactParams> iter = spinList.iterator(); |
| 428 | 0 | ContactParams cp = new ContactParams(cid, setName, size, contactNumber); |
| 429 | 0 | while (iter.hasNext()) { |
| 430 | 0 | ContactParams cpl = iter.next(); |
| 431 | 0 | if (cpl.equals(cp)) |
| 432 | 0 | return(true); |
| 433 | 0 | } |
| 434 | 0 | return(false); |
| 435 | } | |
| 436 | private void removeFromSpin(ContactParams cp) { | |
| 437 | if (DEBUGSPIN) | |
| 438 | logger.info("removeFromSpin: cp cid=" + cp.getCid() + " ss= " + cp.getSetName() + "_" + cp.getSize() + " cn=" + cp.getContactNumber()); | |
| 439 | 0 | Iterator<ContactParams> iter = spinList.iterator(); |
| 440 | 0 | while (iter.hasNext()) { |
| 441 | 0 | ContactParams cpl = iter.next(); |
| 442 | 0 | if (cpl.equals(cp)) { |
| 443 | 0 | spinList.remove(cpl); |
| 444 | 0 | return; |
| 445 | } | |
| 446 | 0 | } |
| 447 | 0 | } |
| 448 | } |