Java tutorial
/* * Copyright 2008 Glencoe Software, Inc. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package ome.services.sharing; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import ome.api.IShare; import ome.conditions.OptimisticLockException; import ome.model.IObject; import ome.model.core.Channel; import ome.model.core.Image; import ome.model.core.LogicalChannel; import ome.model.core.Pixels; import ome.model.core.PlaneInfo; import ome.model.display.ChannelBinding; import ome.model.display.QuantumDef; import ome.model.display.RenderingDef; import ome.model.display.Thumbnail; import ome.model.meta.Experimenter; import ome.model.meta.Share; import ome.model.meta.ShareMember; import ome.model.stats.StatsInfo; import ome.model.acquisition.Detector; import ome.model.acquisition.DetectorSettings; import ome.model.acquisition.Dichroic; import ome.model.acquisition.Filter; import ome.model.acquisition.FilterSet; import ome.model.acquisition.Instrument; import ome.model.acquisition.Laser; import ome.model.acquisition.LightPath; import ome.model.acquisition.LightSettings; import ome.model.acquisition.LightSource; import ome.model.acquisition.Microscope; import ome.model.acquisition.Objective; import ome.model.acquisition.ObjectiveSettings; import ome.model.acquisition.TransmittanceRange; import ome.services.sharing.data.Obj; import ome.services.sharing.data.ShareData; import ome.services.sharing.data.ShareItem; import ome.system.OmeroContext; import ome.tools.hibernate.QueryBuilder; import ome.tools.hibernate.SessionFactory; import org.hibernate.Query; import org.hibernate.Session; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.dao.EmptyResultDataAccessException; /** * * * @author Josh Moore, josh at glencoesoftware.com * @since 3.0-Beta4 * @see IShare */ public class BlobShareStore extends ShareStore implements ApplicationContextAware { /** * Used <em>indirectly</em> to obtain sessions for querying and updating the * store during normal operation. Due to this classes late initialization, * all sessions should be obtained from {@link #session()}. */ protected SessionFactory __dont_use_me_factory; protected OmeroContext ctx; /** * Because there is a cyclical dependency SF->ACLVoter->BlobStore->SF we * have to lazy-load the session factory via the context. */ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.ctx = (OmeroContext) applicationContext; } // Initialization/Destruction @Override public void doInit() { // Currently nothing. } // Overrides // ========================================================================= @Override public Long totalShares() { return (Long) session().createQuery("select count(id) from Share").uniqueResult(); } @Override public Long totalSharedItems() { return (Long) session().createQuery("select sum(itemCount) from Share").uniqueResult(); } @Override public void doSet(Share share, ShareData data, List<ShareItem> items) { long oldOptLock = data.optlock; long newOptLock = oldOptLock + 1; Session session = session(); List list = session .createQuery("select s from Share s where s.id = " + data.id + " and s.version =" + data.optlock) .list(); if (list.size() == 0) { throw new OptimisticLockException("Share " + data.id + " has been updated by someone else."); } data.optlock = newOptLock; share.setData(parse(data)); share.setActive(data.enabled); share.setItemCount((long) items.size()); share.setVersion((int) newOptLock); session.merge(share); synchronizeMembers(session, data); } @Override public ShareData get(final long id) { Session session = session(); Share s = (Share) session.get(Share.class, id); if (s == null) { return null; } byte[] data = s.getData(); return parse(id, data); } @Override public List<ShareData> getShares(long userId, boolean own, boolean activeOnly) { Session session = session(); QueryBuilder qb = new QueryBuilder(); qb.select("share.id"); qb.from("ShareMember", "sm"); qb.join("sm.parent", "share", false, false); qb.where(); qb.and("sm.child.id = :userId"); qb.param("userId", userId); if (own) { qb.and("share.owner.id = sm.child.id"); } else { qb.and("share.owner.id != sm.child.id"); } if (activeOnly) { qb.and("share.active is true"); } Query query = qb.query(session); List<Long> shareIds = query.list(); if (shareIds.size() == 0) { return new ArrayList<ShareData>(); // EARLY EXIT! } List<ShareData> rv = new ArrayList<ShareData>(); try { Map<Long, byte[]> data = data(shareIds); for (Long id : data.keySet()) { byte[] bs = data.get(id); ShareData d = parse(id, bs); rv.add(d); } return rv; } catch (EmptyResultDataAccessException empty) { return null; } } boolean imagesContainsPixels(Session s, List<Long> images, Pixels pix, Map<Long, Long> cache) { Long pixID = pix.getId(); return imagesContainsPixels(s, images, pixID, cache); } boolean imagesContainsPixels(Session s, List<Long> images, long pixID, Map<Long, Long> cache) { Long imgID; if (cache.containsKey(pixID)) { imgID = cache.get(pixID); } else { imgID = (Long) s.createQuery("select image.id from Pixels where id = ?").setParameter(0, pixID) .uniqueResult(); cache.put(pixID, imgID); } return images.contains(imgID); } boolean imagesContainsInstrument(Session s, List<Long> images, Instrument instr, Map<Long, Long> cache) { if (instr == null) { return false; } Long instrID = instr.getId(); Long imgID; if (cache.containsKey(instrID)) { imgID = cache.get(instrID); } else { imgID = (Long) s.createQuery("select id from Image where instrument.id = ?").setParameter(0, instrID) .uniqueResult(); cache.put(instrID, imgID); } return images.contains(imgID); } boolean imagesContainsObjectiveSettings(Session s, List<Long> images, ObjectiveSettings os, Map<Long, Long> cache) { Long osID = os.getId(); return imagesContainsObjectiveSettings(s, images, osID, cache); } boolean imagesContainsObjectiveSettings(Session s, List<Long> images, long osID, Map<Long, Long> cache) { Long imgID; if (cache.containsKey(osID)) { imgID = cache.get(osID); } else { imgID = (Long) s.createQuery("select id from Image where objectivesettings.id = ?") .setParameter(0, osID).uniqueResult(); cache.put(osID, imgID); } return images.contains(imgID); } @Override public <T extends IObject> boolean doContains(long sessionId, Class<T> kls, long objId) { ShareData data = get(sessionId); if (data == null) { return false; } return doContains(data, kls, objId); } protected <T extends IObject> boolean doContains(ShareData data, Class<T> kls, long objId) { List<Long> ids = data.objectMap.get(kls.getName()); if (ids != null && ids.contains(objId)) { return true; } // ticket:2249 - Implementing logic similar to the query // in DeleteBean in order to allow all objects which link // back to an Image to also be loaded. /* + "left outer join fetch i.pixels as p " + "left outer join fetch p.channels as c " + "left outer join fetch c.logicalChannel as lc " + "left outer join fetch lc.channels as c2 " + "left outer join fetch c.statsInfo as sinfo " + "left outer join fetch p.planeInfo as pinfo " + "left outer join fetch p.thumbnails as thumb " + "left outer join fetch p.pixelsFileMaps as map " + "left outer join fetch map.parent as ofile " + "left outer join fetch p.settings as setting " // rdef + "left outer join fetch r.waveRendering " + "left outer join fetch r.quantization " */ List<Long> images = data.objectMap.get(Image.class.getName()); Map<Long, Long> pixToImageCache = new HashMap<Long, Long>(); Session s = session(); if (Pixels.class.isAssignableFrom(kls)) { return imagesContainsPixels(s, images, objId, pixToImageCache); } else if (RenderingDef.class.isAssignableFrom(kls)) { RenderingDef obj = (RenderingDef) s.get(RenderingDef.class, objId); return imagesContainsPixels(s, images, obj.getPixels(), pixToImageCache); } else if (ChannelBinding.class.isAssignableFrom(kls)) { ChannelBinding obj = (ChannelBinding) s.get(ChannelBinding.class, objId); return imagesContainsPixels(s, images, obj.getRenderingDef().getPixels(), pixToImageCache); } else if (Thumbnail.class.isAssignableFrom(kls)) { Thumbnail obj = (Thumbnail) s.get(Thumbnail.class, objId); return imagesContainsPixels(s, images, obj.getPixels(), pixToImageCache); } else if (Channel.class.isAssignableFrom(kls)) { Channel obj = (Channel) s.get(Channel.class, objId); return imagesContainsPixels(s, images, obj.getPixels(), pixToImageCache); } else if (LogicalChannel.class.isAssignableFrom(kls)) { LogicalChannel obj = (LogicalChannel) s.get(LogicalChannel.class, objId); Iterator<Channel> it = obj.iterateChannels(); while (it.hasNext()) { Channel ch = it.next(); if (images.contains(ch.getPixels().getImage().getId())) { return true; } } } else if (PlaneInfo.class.isAssignableFrom(kls)) { PlaneInfo obj = (PlaneInfo) s.get(PlaneInfo.class, objId); return imagesContainsPixels(s, images, obj.getPixels(), pixToImageCache); } else if (StatsInfo.class.isAssignableFrom(kls) || QuantumDef.class.isAssignableFrom(kls) || LightPath.class.isAssignableFrom(kls) || Microscope.class.isAssignableFrom(kls) || TransmittanceRange.class.isAssignableFrom(kls)) { // Objects we just don't care about so let the // user load them if they really want to. return true; } Map<Long, Long> obToImageCache = new HashMap<Long, Long>(); if (Objective.class.isAssignableFrom(kls)) { Objective obj = (Objective) s.get(Objective.class, objId); return imagesContainsInstrument(s, images, obj.getInstrument(), obToImageCache); } else if (Detector.class.isAssignableFrom(kls)) { Detector obj = (Detector) s.get(Detector.class, objId); return imagesContainsInstrument(s, images, obj.getInstrument(), obToImageCache); } else if (Dichroic.class.isAssignableFrom(kls)) { Dichroic obj = (Dichroic) s.get(Dichroic.class, objId); return imagesContainsInstrument(s, images, obj.getInstrument(), obToImageCache); } else if (FilterSet.class.isAssignableFrom(kls)) { FilterSet obj = (FilterSet) s.get(FilterSet.class, objId); return imagesContainsInstrument(s, images, obj.getInstrument(), obToImageCache); } else if (Filter.class.isAssignableFrom(kls)) { Filter obj = (Filter) s.get(Filter.class, objId); return imagesContainsInstrument(s, images, obj.getInstrument(), obToImageCache); } else if (LightSource.class.isAssignableFrom(kls)) { LightSource obj = (LightSource) s.get(LightSource.class, objId); return imagesContainsInstrument(s, images, obj.getInstrument(), obToImageCache); } else if (Laser.class.isAssignableFrom(kls)) { Laser obj = (Laser) s.get(Laser.class, objId); return imagesContainsInstrument(s, images, obj.getInstrument(), obToImageCache); } else if (LightSettings.class.isAssignableFrom(kls)) { LightSettings obj = (LightSettings) s.get(LightSettings.class, objId); return imagesContainsInstrument(s, images, obj.getLightSource().getInstrument(), obToImageCache); } else if (DetectorSettings.class.isAssignableFrom(kls)) { DetectorSettings obj = (DetectorSettings) s.get(DetectorSettings.class, objId); if (imagesContainsInstrument(s, images, obj.getDetector().getInstrument(), obToImageCache)) { return true; } else { List<LogicalChannel> lcs = s .createQuery("select l from LogicalChannel l where l.detectorSettings.id = " + obj.getId()) .list(); for (LogicalChannel lc : lcs) { if (doContains(data, LogicalChannel.class, lc.getId())) { return true; } } } } return false; } @Override public void doClose() { // no-op } @Override @SuppressWarnings("unchecked") public Set<Long> keys() { Session session = session(); List list = session.createQuery("select id from Share").list(); return new HashSet<Long>(list); } // Helpers // ========================================================================= /** * Returns a list of data from all shares. * * @return */ @SuppressWarnings("unchecked") private Map<Long, byte[]> data(List<Long> ids) { Session session = session(); Query q = session.createQuery("select id, data from Share where id in (:ids)"); q.setParameterList("ids", ids); List<Object[]> data = q.list(); Map<Long, byte[]> rv = new HashMap<Long, byte[]>(); for (Object[] objects : data) { rv.put((Long) objects[0], (byte[]) objects[1]); } return rv; } private Session session() { return initialize().getSession(); } /** * Loads the {@link SessionFactory}. This is the only method which should * access the {@link #__dont_use_me_factory} instance variable, since it * guarantees loading. Any direct access may well throw a * {@link NullPointerException} */ private synchronized SessionFactory initialize() { if (__dont_use_me_factory != null) { return __dont_use_me_factory; // GOOD! } if (ctx == null) { throw new IllegalStateException("Have no context to load factory"); } __dont_use_me_factory = (SessionFactory) ctx.getBean("omeroSessionFactory"); if (__dont_use_me_factory == null) { throw new IllegalStateException("Cannot find factory"); } // Finally calling init here, since before it's not possible init(); return __dont_use_me_factory; } private void synchronizeMembers(Session session, ShareData data) { Query q = session.createQuery("select sm from ShareMember sm " + "where sm.parent = ?"); q.setLong(0, data.id); List<ShareMember> members = q.list(); Map<Long, ShareMember> lookup = new HashMap<Long, ShareMember>(); for (ShareMember sm : members) { lookup.put(sm.getChild().getId(), sm); } Set<Long> intendedUserIds = new HashSet<Long>(data.members); intendedUserIds.add(data.owner); Set<Long> currentUserIds = lookup.keySet(); Set<Long> added = new HashSet<Long>(intendedUserIds); added.removeAll(currentUserIds); for (Long toAdd : added) { ShareMember sm = new ShareMember(); sm.link(new Share(data.id, false), new Experimenter(toAdd, false)); session.merge(sm); } Set<Long> removed = new HashSet<Long>(currentUserIds); removed.removeAll(intendedUserIds); for (Long toRemove : removed) { session.delete(lookup.get(toRemove)); } } public static void main(String[] args) throws Exception { final BlobShareStore store = new BlobShareStore(); final ShareData template = new ShareData(); template.enabled = true; template.guests = Arrays.asList("a", "b", "c"); template.id = 100; template.members = Arrays.asList(1L, 2L, 3L); template.objectList = Arrays.asList(new Obj("type", 200L)); template.objectMap = new HashMap<String, List<Long>>(); template.objectMap.put("type", Arrays.asList(100L)); template.optlock = 12345; template.owner = 67890; File file = new File(args[0]); if (file.exists()) { int size = (int) file.length(); byte[] buf = new byte[size]; FileInputStream fis = new FileInputStream(file); fis.read(buf); ShareData data = store.parse(1, buf); if (data == null) { System.out.println("No share found"); System.exit(100); } System.out.println("enabled:" + data.enabled); System.out.println("guests:" + data.guests); System.out.println("id:" + data.id); System.out.println("members:" + data.members); System.out.println("objectList:" + data.objectList); System.out.println("objectMap:" + data.objectMap); System.out.println("optlock:" + data.optlock); System.out.println("owner:" + data.owner); } else { FileOutputStream fos = null; try { fos = new FileOutputStream(file); byte[] buf = store.parse(template); fos.write(buf); } finally { if (fos != null) { try { fos.close(); } catch (Exception e) { e.printStackTrace(); } } } } System.exit(0); } }