/* * Copyright 2008 - 2014 Glencoe Software, Inc. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package ome.services.sharing; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import ome.api.IShare; import ome.conditions.ValidationException; import ome.model.IObject; import ome.model.meta.Share; import ome.services.sharing.data.Obj; import ome.services.sharing.data.ShareData; import ome.services.sharing.data.ShareItem; import ome.services.util.IceUtil; import ome.tools.hibernate.QueryBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.orm.hibernate3.HibernateTemplate; import Ice.MarshalException; import Ice.ReadObjectCallback; import Ice.UnmarshalOutOfBoundsException; /** * Entry to the Ice code generated data/ directory. Subclasess of * {@link ShareStore} know how to efficiently store and look up * {@link ShareData} instances. * * @author Josh Moore, josh at glencoesoftware.com * @since 3.0-Beta4 * @see IShare */ public abstract class ShareStore { final protected Logger log = LoggerFactory.getLogger(this.getClass()); final protected Ice.Communicator ic = Ice.Util.initialize(); // User Methods // ========================================================================= /** * Loads share and checks its owner and member data against the current * context (owner/member/admin). This method must be kept in sync with * {@link ShareBean#applyIfShareAccessible(QueryBuilder)} which does the same check * at the database rather than binary data level. */ public ShareData getShareIfAccessible(long shareId, boolean isAdmin, long userId) { ShareData data = get(shareId); if (data == null) { return null; } if (data.owner == userId || data.members.contains(userId) || isAdmin) { return data; } return null; } public <T extends IObject> ShareData set(Share share, long owner, List<T> objects, List<Long> members, List<String> guests, boolean enabled) { ShareData data = new ShareData(); data.id = share.getId(); data.owner = owner; data.members = new ArrayList<Long>(members); data.guests = new ArrayList<String>(guests); data.enabled = enabled; data.objectMap = map(objects); data.objectList = list(data.objectMap); List<ShareItem> shareItems = asItems(share.getId(), data.objectList, members, guests); doSet(share, data, shareItems); return data; } public void update(Share share, ShareData data) { List<ShareItem> shareItems = asItems(data); doSet(share, data, shareItems); } // Parsing // ========================================================================= public final byte[] parse(ShareData data) { Ice.OutputStream os = IceUtil.createSafeOutputStream(ic); byte[] bytes = null; try { os.writeObject(data); os.writePendingObjects(); bytes = os.finished(); } finally { os.destroy(); } return bytes; } public final ShareData parse(long id, byte[] data) { if (data == null) { return null; // EARLY EXIT! } Ice.InputStream is = IceUtil.createSafeInputStream(ic, data); final ShareData[] shareData = new ShareData[1]; try { is.readObject(new ReadObjectCallback() { public void invoke(Ice.Object arg0) { shareData[0] = (ShareData) arg0; } }); is.readPendingObjects(); } catch (UnmarshalOutOfBoundsException oob) { log.error("Share " + id + " is malformed. Creating empty share."); shareData[0] = new ShareData(id, -1L, Collections.<Long> emptyList(), Collections.<String> emptyList(), Collections.<String, List<Long>> emptyMap(), Collections.<Obj> emptyList(), false, 0L); // Eventually we'll need to handle conversion, etc. here or above } catch (MarshalException me) { // Likely a encoding issue. Return a null and let handling code // do what it can with that. log.warn("Share " + id + " cannot be unmarshalled. Returning null."); return null; } finally { is.destroy(); } return shareData[0]; } // Template methods // ========================================================================= /** * Calls {@link #doInit()} within a transaction with a session available to * all {@link HibernateTemplate} callbacks. */ public final void init() { doInit(); Long mapsize = totalShares(); Long itemssize = totalSharedItems(); log.info("Loaded store " + this + " with " + mapsize + " shares and " + itemssize + " objects"); } public final void close() { try { doClose(); } finally { ic.destroy(); } } public final <T extends IObject> boolean contains(long sessionId, Class<T> kls, long objId) { return doContains(sessionId, kls, objId); } // Abstract Methods // ========================================================================= public abstract void doInit(); public abstract Long totalShares(); public abstract Long totalSharedItems(); public abstract Set<Long> keys(); public abstract ShareData get(long id); public abstract List<ShareData> getShares(long userId, boolean own, boolean active); public abstract <T extends IObject> boolean doContains(long sessionId, Class<T> kls, long objId); public abstract void doSet(Share share, ShareData data, List<ShareItem> items); public abstract void doClose(); // Helper Methods // ========================================================================= private <T extends IObject> List<ShareItem> asItems(long share, List<Obj> items, List<Long> members, List<String> guests) { List<ShareItem> shareItems = new ArrayList<ShareItem>(items.size()); for (Obj item : items) { ShareItem shareItem = new ShareItem(); shareItem.share = share; shareItem.id = item.id; shareItem.type = item.type; shareItem.members = new ArrayList<Long>(members); shareItem.guests = new ArrayList<String>(guests); shareItems.add(shareItem); } return shareItems; } private <T extends IObject> List<ShareItem> asItems(ShareData data) { Map<String, List<Long>> map = data.objectMap; List<ShareItem> shareItems = new ArrayList<ShareItem>(); for (String type : map.keySet()) { for (Long id : map.get(type)) { ShareItem shareItem = new ShareItem(); shareItem.share = data.id; shareItem.id = id; shareItem.type = type; shareItem.members = data.members; shareItem.guests = data.guests; shareItems.add(shareItem); } } return shareItems; } /** * Treats the List<Long> of ids as a set by only adding each once. */ private <T extends IObject> Map<String, List<Long>> map(List<T> items) { Map<String, List<Long>> map = new HashMap<String, List<Long>>(); for (T t : items) { String kls = t.getClass().getName(); List<Long> ids = map.get(kls); if (ids == null) { ids = new ArrayList<Long>(); map.put(kls, ids); } if (!ids.contains(t.getId())) { ids.add(t.getId()); } } return map; } private List<Obj> list(Map<String, List<Long>> items) { List<Obj> objList = new ArrayList<Obj>(); for (String key : items.keySet()) { List<Long> ids = items.get(key); for (Long id : ids) { if (id == null) { throw new ValidationException( "Cannot add object with null id!"); } Obj obj = new Obj(); obj.type = key; obj.id = id; objList.add(obj); } } return objList; } }