/* * Copyright 2011 Glencoe Software, Inc. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package ome.services.pixeldata; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import ome.api.IQuery; import ome.conditions.LockTimeout; import ome.io.nio.PixelsService; import ome.model.core.Channel; import ome.model.core.Pixels; import ome.model.meta.Event; import ome.model.meta.EventLog; import ome.model.stats.StatsInfo; import ome.parameters.Parameters; import ome.services.eventlogs.EventLogLoader; import ome.services.util.Executor.SimpleWork; import ome.system.ServiceFactory; import ome.util.SqlAction; import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; /** * Simple action which can be done in an asynchronous thread in order to process * PIXELDATA event logs. * * @author Josh Moore, josh at glencoesoftware.com * @since Beta4.3 */ public class PixelDataHandler extends SimpleWork { private final static Logger log = LoggerFactory.getLogger(PixelDataHandler.class); final protected EventLogLoader loader; final protected PixelsService pixelsService; protected int reps = 5; /** * Spring injector. Sets the number of indexing runs will be made if there * is a substantial backlog. */ public void setRepetitions(int reps) { this.reps = reps; ; } public PixelDataHandler(EventLogLoader ll, PixelsService pixelsService) { super("PixelDataHandler", "process"); this.loader = ll; this.pixelsService = pixelsService; } /** * Since these instances are used repeatedly, we need to check for * already set SqlAction */ @Override public synchronized void setSqlAction(SqlAction sql) { if (getSqlAction() == null) { super.setSqlAction(sql); } } /** * Loads {@link #reps} {@link EventLog} instances and returns them. * This is the first phase used by the {@link PixelDataThread}. A later * phase will invoke {@link #handleEventLog(EventLog, Session, ServiceFactory)} * with the returned instance. */ @Transactional(readOnly = false) public Object doWork(Session session, ServiceFactory sf) { List<EventLog> logs = new ArrayList<EventLog>(); while (logs.size() < this.reps) { try { logs.add(loader.next()); } catch (NoSuchElementException nsee) { if (!loader.hasNext()) { break; }; } } // Preload for (EventLog el : logs) { EventLog live = (EventLog) session.get(EventLog.class, el.getId()); Event evt = live.getEvent(); el.setEvent(evt); } return logs; } /** * Handles only single elements from the {@link PersistentEventLogLoader} * in order to keep transactions short and safe. * * @see <a href="http://trac.openmicroscopy.org/ome/ticket/5814">ticket:5814</a> */ public void handleEventLog(EventLog eventLog, Session session, ServiceFactory sf) { final long start = System.currentTimeMillis(); final boolean handled = process(eventLog.getEntityId(), sf, session); final String msg = String.format("EventLog:%s(entityId=%s) [%s ms.]", eventLog.getId(), eventLog.getEntityId(), (System.currentTimeMillis() - start)); if (handled) { log.info("HANDLED "+ msg); } else { log.debug("SKIPPED "+ msg); } } /** * Here we assume that our log loader will only return * us the proper types, since we are using the specific * type defined in this package. * * @param id * @param sf * @param s * @return See above. */ public boolean process(Long id, ServiceFactory sf, Session s) { final Pixels pixels = getPixels(id, sf); if (pixels == null) { log.error("No valid pixels found with id=" + id); return false; } try { StatsInfo[] statsInfo = pixelsService.makePyramid(pixels); if(statsInfo == null) { // Either exists or failed to be created, but that's // the PixelsService's business. It should throw an exception // if it wants us to more concretely handle any issues. log.debug("No min/max values for pixels " + id); return false; } for(int c=0;c<statsInfo.length;c++) { final StatsInfo si = statsInfo[c]; final Channel ch = pixels.getChannel(c); long siId = getSqlAction().setStatsInfo(ch, si); log.info(String.format("Added StatsInfo:%s for %s - C:%s Max:%s Min:%s", siId, ch, c, si.getGlobalMax(), si.getGlobalMin())); } } catch (LockTimeout lt) { log.warn("Pixels:" + id + " -- " + lt.getMessage()); return false; } catch (Exception t) { log.error("Failed to handle pixels " + id, t); return false; } return true; } protected Pixels getPixels(Long id, ServiceFactory sf) { final IQuery iQuery = sf.getQueryService(); final Pixels pixels = iQuery.findByQuery( "select p from Pixels as p " + "left outer join fetch p.channels ch " + // For statsinfo "join fetch p.pixelsType where p.id = :id ", new Parameters().addId(id)); return pixels; } }