• Views
  • Iteration Report
  • My Iteration Report
  •  
OMERO.server
  • Login
  • Help/Guide
  • About Trac
  • Preferences
  • Wiki
  • Timeline
  • Roadmap
  • Browse Source
  • View Tickets
  • Search

Context Navigation

  • ← Previous Changeset
  • Next Changeset →

Changeset 2592

Show
Ignore:
Timestamp:
07/05/08 22:16:04 (3 months ago)
Author:
jmoore
Message:

ticket:1018 - Initial version of OmeroSessions reference counting

Implementation is incomplete in the blitz case since there are two
possible "closers" - the user and the glacier session, which must
be taken into account.

Location:
trunk/components
Files:
8 modified

  • blitz/src/ome/services/blitz/impl/ServiceFactoryI.java (modified) (1 diff)
  • server/src/ome/services/sessions/SessionContext.java (modified) (1 diff)
  • server/src/ome/services/sessions/SessionContextImpl.java (modified) (2 diffs)
  • server/src/ome/services/sessions/SessionManager.java (modified) (1 diff)
  • server/src/ome/services/sessions/SessionManagerImpl.java (modified) (8 diffs)
  • server/test/ome/server/utests/sessions/SessMgrUnitTest.java (modified) (11 diffs)
  • tools/OmeroPy/src/omero/__init__.py (modified) (5 diffs)
  • tools/OmeroPy/test/integration/isession.py (modified) (1 diff)

Legend:

Unmodified
Added
Removed
  • trunk/components/blitz/src/ome/services/blitz/impl/ServiceFactoryI.java

    r2397 r2592  
    524524    } 
    525525 
     526    /** 
     527     * Destruction simply decrements the reference count for a session to allow 
     528     * reconnecting to it. This means that the Glacier timeout property is 
     529     * fairly unimportant. If a Glacier connection times out or is otherwise 
     530     * destroyed, a client can attempt to reconnect 
     531     */ 
    526532    public void destroy(Ice.Current current) { 
    527         if (log.isInfoEnabled()) { 
    528             log.info(String.format("Destroying %s session", current.id.name)); 
    529         } 
    530         close(current); 
     533        sessionManager.detach(this.principal.getName()); 
    531534    } 
    532535 
  • trunk/components/server/src/ome/services/sessions/SessionContext.java

    r2124 r2592  
    2424 
    2525    Session getSession(); 
     26 
    2627    List<String> getUserRoles(); 
    2728 
     29    // Reference counting 
     30 
     31    /** 
     32     * Return the current number of references which this session is aware of. 
     33     */ 
     34    int refCount(); 
     35 
     36    /** 
     37     * Increment the current {@link #refCount() reference count} and return the 
     38     * new value atomically. 
     39     */ 
     40    int increment(); 
     41 
     42    /** 
     43     * Decrement the current {@link #refCount() reference count} and return the 
     44     * new value atomically. 
     45     */ 
     46    int decrement(); 
    2847} 
  • trunk/components/server/src/ome/services/sessions/SessionContextImpl.java

    r2125 r2592  
    1111import java.util.Collections; 
    1212import java.util.List; 
     13import java.util.concurrent.atomic.AtomicInteger; 
    1314 
    1415import ome.model.meta.Session; 
    1516 
    1617public class SessionContextImpl implements SessionContext { 
     18 
     19    private final AtomicInteger refCount = new AtomicInteger(0); 
    1720    private final Session session; 
    1821    private final List<Long> leaderOfGroups; 
    … …  
    2831                mGroups)); 
    2932        this.roles = Collections.unmodifiableList(new ArrayList(roles)); 
     33    } 
     34 
     35    public int refCount() { 
     36        return refCount.get(); 
     37    } 
     38 
     39    public int increment() { 
     40        return refCount.incrementAndGet(); 
     41    } 
     42 
     43    public int decrement() { 
     44        return refCount.decrementAndGet(); 
    3045    } 
    3146 
  • trunk/components/server/src/ome/services/sessions/SessionManager.java

    r2274 r2592  
    5151    Session create(Principal principal); 
    5252 
     53    /** 
     54     *  
     55     * @param session 
     56     * @return 
     57     */ 
    5358    Session update(Session session); 
     59 
     60    /** 
     61     * Allows decrementing the reference count for a session without calling the 
     62     * actual {@link #close(String)} logic. This is useful when it is assumed 
     63     * that another user will re-attach to the same session. A timeout can still 
     64     * cause the session to be removed. 
     65     *  
     66     * @param uuid 
     67     * @return 
     68     */ 
     69    int detach(String uuid); 
    5470 
    5571    /** 
  • trunk/components/server/src/ome/services/sessions/SessionManagerImpl.java

    r2388 r2592  
    7373 
    7474    // Injected 
    75     OmeroContext context; 
    76     Roles roles; 
    77     SessionCache cache; 
    78     Executor executor; 
    79     long defaultTimeToIdle; 
    80     long defaultTimeToLive; 
     75    protected OmeroContext context; 
     76    protected Roles roles; 
     77    protected SessionCache cache; 
     78    protected Executor executor; 
     79    protected long defaultTimeToIdle; 
     80    protected long defaultTimeToLive; 
    8181 
    8282    /** 
    … …  
    8484     * {@link Executor} 
    8585     */ 
    86     Principal asroot; 
    87     SessionContext sc; 
     86    protected Principal asroot; 
     87    protected SessionContext sc; 
    8888 
    8989    // ~ Injectors 
    … …  
    155155            SessionContext context = cache.getSessionContext(credentials); 
    156156            if (context != null) { 
     157                context.increment(); 
    157158                return context.getSession(); // EARLY EXIT! 
    158159            } 
    … …  
    161162        } 
    162163 
    163         boolean ok = executeCheckPassword(_principal, credentials); 
     164        // Though trusted values, if we receive a null principal, not ok; 
     165        boolean ok = _principal == null ? false : executeCheckPassword( 
     166                _principal, credentials); 
    164167 
    165168        if (!ok) { 
    … …  
    179182                    .getName()); 
    180183            if (context != null) { 
     184                context.increment(); 
    181185                return context.getSession(); // EARLY EXIT! 
    182186            } 
    … …  
    205209            throw re; 
    206210        } 
     211 
     212        // All successful, increment and return. 
     213        ctx.increment(); 
    207214        return session; 
    208215    } 
    … …  
    294301    } 
    295302 
     303    public int getReferenceCount(String uuid) { 
     304        SessionContext ctx = cache.getSessionContext(uuid); 
     305        return ctx.refCount(); 
     306    } 
     307 
     308    public int detach(String uuid) { 
     309        SessionContext ctx = cache.getSessionContext(uuid); 
     310        return ctx.decrement(); 
     311    } 
     312 
    296313    /* 
    297314     */ 
    … …  
    302319            ctx = cache.getSessionContext(uuid); 
    303320        } catch (SessionException se) { 
    304             ctx = null; 
    305         } 
    306  
    307         if (ctx == null) { 
    308             return; 
    309         } 
    310  
    311         Session s = ctx.getSession(); 
    312         s.setClosed(new Timestamp(System.currentTimeMillis())); 
    313         update(s); 
    314  
    315         try { 
    316             context.publishEvent(new DestroySessionMessage(this, s.getUuid())); 
    317         } catch (RuntimeException re) { 
    318             log.warn("Session destruction cancelled by event listener", re); 
    319             throw re; 
    320         } 
    321         // This the publishEvent returns successfully, then we update our cache 
    322         // since ehcache is not tx-friendly. 
    323         cache.removeSession(uuid); 
     321            return; // EARLY EXIT! 
     322        } 
     323 
     324        int refCount = ctx.decrement(); 
     325        if (refCount < 1) { 
     326 
     327            Session s = ctx.getSession(); 
     328            s.setClosed(new Timestamp(System.currentTimeMillis())); 
     329            update(s); 
     330 
     331            try { 
     332                context.publishEvent(new DestroySessionMessage(this, s 
     333                        .getUuid())); 
     334            } catch (RuntimeException re) { 
     335                log.warn("Session destruction cancelled by event listener", re); 
     336                throw re; 
     337            } 
     338            // This the publishEvent returns successfully, then we update our 
     339            // cache 
     340            // since ehcache is not tx-friendly. 
     341            cache.removeSession(uuid); 
     342        } 
    324343    } 
    325344 
  • trunk/components/server/test/ome/server/utests/sessions/SessMgrUnitTest.java

    r2126 r2592  
    1010import java.util.List; 
    1111 
    12 import ome.conditions.ApiUsageException; 
     12import net.sf.ehcache.CacheManager; 
     13import ome.api.local.LocalAdmin; 
     14import ome.api.local.LocalQuery; 
     15import ome.api.local.LocalUpdate; 
     16import ome.conditions.AuthenticationException; 
    1317import ome.conditions.SecurityViolation; 
    1418import ome.conditions.SessionException; 
    … …  
    2024import ome.model.meta.ExperimenterGroup; 
    2125import ome.model.meta.Session; 
     26import ome.services.sessions.SessionContext; 
    2227import ome.services.sessions.SessionManagerImpl; 
     28import ome.services.sessions.state.SessionCache; 
     29import ome.services.util.Executor; 
     30import ome.system.OmeroContext; 
    2331import ome.system.Principal; 
    2432import ome.system.Roles; 
     33import ome.testing.MockServiceFactory; 
    2534 
    2635import org.jmock.Mock; 
    2736import org.jmock.MockObjectTestCase; 
    2837import org.jmock.core.Constraint; 
     38import org.jmock.core.Invocation; 
     39import org.jmock.core.Stub; 
    2940import org.testng.annotations.BeforeTest; 
    3041import org.testng.annotations.Test; 
    … …  
    3647public class SessMgrUnitTest extends MockObjectTestCase { 
    3748 
    38     private static class TestSessionManager extends SessionManagerImpl { 
    39  
    40     } 
    41  
    42     private Mock adminMock, updateMock, queryMock; 
    43     private TestSessionManager mgr; 
    44     private Session session; 
    45  
    46     @BeforeTest 
    47     public void config() { 
    48         mgr = new TestSessionManager(); 
    49         mgr.setRoles(new Roles()); 
    50  
    51         session = new Session(); 
    52         session.setUuid("uuid"); 
    53         session.setId(1L); 
    54     } 
    55  
     49    private final class DoWorkStub implements Stub { 
     50        public Object invoke(Invocation i) throws Throwable { 
     51            Executor.Work work = (Executor.Work) i.parameterValues.get(1); 
     52            return work.doWork(null, null, sf); 
     53        } 
     54 
     55        public StringBuffer describeTo(StringBuffer sb) { 
     56            sb.append("calls doWork on work"); 
     57            return sb; 
     58        } 
     59    } 
     60 
     61    private final class TestManager extends SessionManagerImpl { 
     62        Session doDefine() { 
     63            return define("uuid", "message", System.currentTimeMillis(), 
     64                    defaultTimeToIdle, defaultTimeToLive, "Test", "rw----"); 
     65        } 
     66    } 
     67 
     68    private final OmeroContext oc = new OmeroContext( 
     69            "classpath:ome/testing/empty.xml"); 
     70    private final MockServiceFactory sf = new MockServiceFactory(); 
     71    private Mock exMock; 
     72    private TestManager mgr; 
     73    private SessionCache cache; 
     74 
     75    // State 
     76    Session session = new Session(); 
    5677    Principal principal = new Principal("u", "g", "Test"); 
    5778    String credentials = "password"; 
    … …  
    6283    List<String> userRoles = Collections.singletonList("single"); 
    6384 
     85    @BeforeTest 
     86    public void config() { 
     87 
     88        exMock = mock(Executor.class); 
     89        sf.mockAdmin = mock(LocalAdmin.class); 
     90        sf.mockUpdate = mock(LocalUpdate.class); 
     91        sf.mockQuery = mock(LocalQuery.class); 
     92 
     93        cache = new SessionCache(); 
     94        cache.setCacheManager(CacheManager.getInstance()); 
     95 
     96        mgr = new TestManager(); 
     97        mgr.setRoles(new Roles()); 
     98        mgr.setSessionCache(cache); 
     99        mgr.setExecutor((Executor) exMock.proxy()); 
     100        mgr.setApplicationContext(oc); 
     101        mgr.setDefaultTimeToIdle(100 * 1000L); 
     102        mgr.setDefaultTimeToLive(300 * 1000L); 
     103 
     104        session = mgr.doDefine(); 
     105        session.setId(1L); 
     106 
     107        user.setOmeName(principal.getName()); 
     108    } 
     109 
    64110    void prepareSessionCreation() { 
    65         adminMock.expects(once()).method("checkPassword").will( 
     111 
     112        exMock.expects(atLeastOnce()).method("execute").will(new DoWorkStub()); 
     113 
     114        sf.mockAdmin.expects(once()).method("checkPassword").will( 
    66115                returnValue(true)); 
    67         adminMock.expects(once()).method("userProxy").will(returnValue(user)); 
    68         adminMock.expects(once()).method("groupProxy").will(returnValue(group)); 
    69         adminMock.expects(once()).method("getMemberOfGroupIds").will( 
     116        sf.mockAdmin.expects(atLeastOnce()).method("userProxy").will( 
     117                returnValue(user)); 
     118        sf.mockAdmin.expects(atLeastOnce()).method("groupProxy").will( 
     119                returnValue(group)); 
     120        sf.mockAdmin.expects(atLeastOnce()).method("getMemberOfGroupIds").will( 
    70121                returnValue(m_ids)); 
    71         adminMock.expects(once()).method("getLeaderOfGroupIds").will( 
     122        sf.mockAdmin.expects(atLeastOnce()).method("getLeaderOfGroupIds").will( 
    72123                returnValue(l_ids)); 
    73         adminMock.expects(once()).method("getUserRoles").will( 
     124        sf.mockAdmin.expects(atLeastOnce()).method("getUserRoles").will( 
    74125                returnValue(userRoles)); 
    75         adminMock.expects(once()).method("checkPassword").will( 
     126        sf.mockAdmin.expects(once()).method("checkPassword").will( 
    76127                returnValue(true)); 
    77         queryMock.expects(once()).method("findAllByQuery") 
     128        sf.mockQuery.expects(once()).method("findAllByQuery") 
    78129                .will( 
    79130                        returnValue(Collections 
    80131                                .singletonList(new ExperimenterGroup()))); 
    81         updateMock.expects(once()).method("saveObject"); 
     132        sf.mockUpdate.expects(atLeastOnce()).method("saveAndReturnObject") 
     133                .will(returnValue(session)); 
     134 
     135    } 
     136 
     137    void prepareSessionUpdate() { 
     138        prepareSessionCreation(); 
     139        // exMock.expects(atLeastOnce()).method("execute").will(new 
     140        // DoWorkStub()); 
     141        // 
     142        // sf.mockAdmin.expects(once()).method("userProxy") 
     143        // .will(returnValue(user)); 
     144        // sf.mockAdmin.expects(atLeastOnce()).method("groupProxy").will( 
     145        // returnValue(group)); 
     146        // sf.mockUpdate.expects(once()).method("saveAndReturnObject").will( 
     147        // new SetIdStub(1L)); 
     148        // 
    82149    } 
    83150 
    … …  
    91158 
    92159        prepareSessionCreation(); 
    93         session = mgr.create(principal, credentials); 
     160        assert session == mgr.create(principal, credentials); 
    94161        assertNotNull(session); 
    95162        assertNotNull(session.getUuid()); 
    … …  
    141208        testCreateNewSession(); 
    142209        session.setDefaultEventType("somethingnew"); 
    143         updateMock.expects(once()).method("saveAndReturnObject").will( 
     210        sf.mockUpdate.expects(once()).method("saveAndReturnObject").will( 
    144211                returnValue(rv)); 
     212 
     213        prepareSessionUpdate(); 
    145214        Session test = mgr.update(session); 
    146         assertTrue(test == rv); // insists that a copy is performed 
     215        assertFalse(test == rv); // insists that a copy is performed 
    147216    } 
    148217 
    … …  
    204273 
    205274        }; 
    206         updateMock.expects(once()).method("saveObject").with(closedSession); 
     275        sf.mockUpdate.expects(once()).method("saveObject").with(closedSession); 
    207276        mgr.close(session.getUuid()); 
    208277        assertNull(mgr.find(session.getUuid())); 
    … …  
    224293 
    225294        }; 
    226         updateMock.expects(once()).method("saveObject").with(closedSession); 
     295        sf.mockUpdate.expects(once()).method("saveObject").with(closedSession); 
    227296        mgr.close(session.getUuid()); 
    228297        assertNull(mgr.find(session.getUuid())); 
    … …  
    260329 
    261330    void prepareForCreateSession() { 
    262         adminMock.expects(once()).method("userProxy").will(returnValue(user)); 
    263         adminMock.expects(once()).method("groupProxy").will(returnValue(group)); 
    264         adminMock.expects(once()).method("getMemberOfGroupIds").will( 
     331        sf.mockAdmin.expects(once()).method("userProxy") 
     332                .will(returnValue(user)); 
     333        sf.mockAdmin.expects(once()).method("groupProxy").will( 
     334                returnValue(group)); 
     335        sf.mockAdmin.expects(atLeastOnce()).method("getMemberOfGroupIds").will( 
    265336                returnValue(m_ids)); 
    266         adminMock.expects(once()).method("getLeaderOfGroupIds").will( 
     337        sf.mockAdmin.expects(atLeastOnce()).method("getLeaderOfGroupIds").will( 
    267338                returnValue(l_ids)); 
    268         adminMock.expects(once()).method("getUserRoles").will( 
     339        sf.mockAdmin.expects(once()).method("getUserRoles").will( 
    269340                returnValue(userRoles)); 
    270         adminMock.expects(once()).method("checkPassword").will( 
     341        sf.mockAdmin.expects(once()).method("checkPassword").will( 
    271342                returnValue(true)); 
    272343    } 
    273344 
    274     @Test(expectedExceptions = ApiUsageException.class) 
     345    @Test(expectedExceptions = AuthenticationException.class) 
    275346    public void testCreateSessionFailsAUEOnNullPrincipal() throws Exception { 
    276         adminMock.expects(once()).method("checkPassword").will( 
     347        sf.mockAdmin.expects(once()).method("checkPassword").will( 
    277348                returnValue(false)); 
    278349        mgr.create(null, "password"); 
    279350    } 
    280351 
    281     @Test(expectedExceptions = ApiUsageException.class) 
     352    @Test(expectedExceptions = AuthenticationException.class) 
    282353    public void testCreateSessionFailsAUEOnNullOmeName() throws Exception { 
    283         adminMock.expects(once()).method("checkPassword").will( 
     354        sf.mockAdmin.expects(once()).method("checkPassword").will( 
    284355                returnValue(false)); 
    285356        mgr.create(new Principal(null, null, null), "password"); 
    286357    } 
    287358 
    288     @Test(expectedExceptions = SecurityViolation.class) 
     359    @Test(expectedExceptions = AuthenticationException.class) 
    289360    public void testCreateSessionFailsSV() throws Exception { 
    290         adminMock.expects(once()).method("checkPassword").will( 
     361        sf.mockAdmin.expects(once()).method("checkPassword").will( 
    291362                returnValue(false)); 
    292363        mgr.create(principal, "password"); 
    … …  
    296367    public void testChecksForDefaultGroupsOnCreation() throws Exception { 
    297368        prepareForCreateSession(); 
    298         queryMock.expects(once()).method("findAllByQuery").will( 
     369        sf.mockAdmin.expects(once()).method("getDefaultGroup").will( 
     370                returnValue(group)); 
     371        sf.mockQuery.expects(once()).method("findAllByQuery").will( 
    299372                returnValue(Collections.EMPTY_LIST)); 
    300373        mgr.create(new Principal("user", "user", "User"), "user"); 
    … …  
    321394        fail("NYI"); 
    322395    } 
     396 
     397    @Test 
     398    public void testReferenceCounting() throws Exception { 
     399        testCreateNewSession(); 
     400        String uuid = session.getUuid(); 
     401        SessionContext ctx = cache.getSessionContext(uuid); 
     402 
     403        assertEquals(1, ctx.refCount()); 
     404        assertNull(ctx.getSession().getClosed()); 
     405 
     406        mgr.create(new Principal(uuid)); 
     407        assertEquals(2, ctx.refCount()); 
     408        assertNull(ctx.getSession().getClosed()); 
     409 
     410        mgr.close(uuid); 
     411        assertEquals(1, ctx.refCount()); 
     412        assertNull(ctx.getSession().getClosed()); 
     413 
     414        prepareSessionUpdate(); 
     415        mgr.close(uuid); 
     416        assertEquals(0, ctx.refCount()); 
     417        assertNotNull(ctx.getSession().getClosed()); 
     418    } 
    323419} 
  • trunk/components/tools/OmeroPy/src/omero/__init__.py

    r2396 r2592  
    1111""" 
    1212 
    13 import exceptions 
     13import exceptions, traceback 
    1414import Ice, Glacier2 
    1515import api 
    … …  
    2121class client(object): 
    2222    """ 
    23     Central blitz entry point 
     23    Central blitz entry point. Currently useful for a single session, after closing the 
     24    connection, create another instance. 
    2425 
    2526    Typical usage includes: 
    … …  
    3738        self.of.registerObjectFactory(ic) 
    3839        self.ic = ic 
     40        self.close_on_destroy = True 
     41 
     42    def closeOnDestroy(self): 
     43        """ 
     44        Closes the blitz session when destroyConnection() is called on exit. 
     45        This prevents other clients from attaching to the same blitz session, 
     46        and is more secure. 
     47        """ 
     48        self.close_on_destroy = True 
     49 
     50    def detachOnDestroy(self): 
     51        """ 
     52        Prevents the blitz session from being closed when destroyConnection() 
     53        is called. This will allow other clients to attach to the session for 
     54        distribtued work. This is less secure, but in many cases valuable. 
     55        """ 
    3956        self.close_on_destroy = False 
    4057 
    41     def closeOnDestroy(self): 
    42         self.close_on_destroy = True 
    43  
    4458    def __del__(self): 
    45         if self.close_on_destroy: 
    46             self.closeSession() 
    47         if self.ic: 
    48             try: 
    49                 self.ic.destroy() 
    50             except (), msg: 
    51                 pysys.stderr.write("Ice exception while destroying communicator:") 
    52                 pysys.stderr.write(msg) 
    53             self.ic = None 
     59        try: 
     60            self.destroyConnection() 
     61        except exceptions.Exception, e: 
     62            print "Error in __del__:" + str(e.__class__) 
    5463 
    5564    def getCommunicator(self): 
    … …  
    207216            file.close() 
    208217 
     218    def destroyConnection(self): 
     219        """ 
     220        Closes the Router connection created by createSession(). Due to a bug in Ice, 
     221        only one connection is allowed per communicator, so we also destroy the communicator. 
     222        """ 
     223        # If 'ic' does not exist we don't have anything to do 
     224        if not hasattr(self, 'ic') or not self.ic: 
     225            return 
     226 
     227        if self.close_on_destroy: 
     228            self.closeSession() 
     229 
     230        prx = self.ic.getDefaultRouter() 
     231        router = Glacier2.RouterPrx.checkedCast(prx) 
     232 
     233        # Now destroy the actual session if possible, 
     234        # which will always trigger an exception, 
     235        # regardless of actually being connected or not 
     236        if router: 
     237            try: 
     238                router.destroySession() 
     239            except exceptions.Exception, e: 
     240                pass 
     241                # SNEE happens if we call sf.close() before 
     242                # calling destroySession(). CLE happens since 
     243                # we are disconnecting 
     244 
     245        try: 
     246            self.ic.destroy() 
     247        except (), msg: 
     248            pysys.stderr.write("Ice exception while destroying communicator:") 
     249            pysys.stderr.write(msg) 
     250        self.ic = None 
     251 
    209252    def closeSession(self): 
     253        """ 
     254        Call close on the session (ServiceFactoryPrx) which first decrements 
     255        the reference count. If its reference count becomes 0, then the session 
     256        will be removed from the server. 
     257        """ 
    210258        # If 'sf' does not exist we don't have a session at all 
    211259        if not hasattr(self, 'sf'): 
    …