001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.clustering.wadi;
018    
019    import java.util.Collections;
020    import java.util.HashMap;
021    import java.util.Iterator;
022    import java.util.Map;
023    import java.util.regex.Pattern;
024    
025    import javax.servlet.ServletContext;
026    
027    import org.apache.commons.logging.Log;
028    import org.apache.commons.logging.LogFactory;
029    import org.apache.geronimo.clustering.Node;
030    import org.apache.geronimo.clustering.Session;
031    import org.apache.geronimo.clustering.SessionAlreadyExistException;
032    import org.apache.geronimo.clustering.SessionListener;
033    import org.apache.geronimo.clustering.SessionManager;
034    import org.apache.geronimo.gbean.GBeanInfo;
035    import org.apache.geronimo.gbean.GBeanInfoBuilder;
036    import org.apache.geronimo.gbean.GBeanLifecycle;
037    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
038    import org.codehaus.wadi.Collapser;
039    import org.codehaus.wadi.Contextualiser;
040    import org.codehaus.wadi.Emoter;
041    import org.codehaus.wadi.Evicter;
042    import org.codehaus.wadi.Immoter;
043    import org.codehaus.wadi.Invocation;
044    import org.codehaus.wadi.InvocationException;
045    import org.codehaus.wadi.Manager;
046    import org.codehaus.wadi.ManagerConfig;
047    import org.codehaus.wadi.Motable;
048    import org.codehaus.wadi.PoolableInvocationWrapperPool;
049    import org.codehaus.wadi.SessionPool;
050    import org.codehaus.wadi.Streamer;
051    import org.codehaus.wadi.group.Dispatcher;
052    import org.codehaus.wadi.impl.AbsoluteEvicter;
053    import org.codehaus.wadi.impl.ClusterContextualiser;
054    import org.codehaus.wadi.impl.ClusteredManager;
055    import org.codehaus.wadi.impl.DummyContextualiser;
056    import org.codehaus.wadi.impl.HashingCollapser;
057    import org.codehaus.wadi.impl.HybridRelocater;
058    import org.codehaus.wadi.impl.MemoryContextualiser;
059    import org.codehaus.wadi.impl.SerialContextualiserFrontingMemory;
060    import org.codehaus.wadi.impl.SimpleSessionPool;
061    import org.codehaus.wadi.impl.SimpleStreamer;
062    import org.codehaus.wadi.impl.SimpleValuePool;
063    import org.codehaus.wadi.impl.StatelessContextualiser;
064    import org.codehaus.wadi.replication.contextualizer.ReplicaAwareContextualiser;
065    import org.codehaus.wadi.replication.manager.ReplicaterAdapterFactory;
066    import org.codehaus.wadi.replication.manager.ReplicationManager;
067    import org.codehaus.wadi.replication.manager.ReplicationManagerFactory;
068    import org.codehaus.wadi.replication.manager.basic.SessionReplicationManager;
069    import org.codehaus.wadi.replication.storage.ReplicaStorage;
070    import org.codehaus.wadi.replication.storage.ReplicaStorageFactory;
071    import org.codehaus.wadi.replication.strategy.BackingStrategyFactory;
072    import org.codehaus.wadi.servicespace.ServiceRegistry;
073    import org.codehaus.wadi.servicespace.ServiceSpaceName;
074    import org.codehaus.wadi.servicespace.basic.BasicServiceSpace;
075    import org.codehaus.wadi.web.WebSession;
076    import org.codehaus.wadi.web.WebSessionPool;
077    import org.codehaus.wadi.web.impl.AtomicallyReplicableSessionFactory;
078    import org.codehaus.wadi.web.impl.DistributableAttributesFactory;
079    import org.codehaus.wadi.web.impl.DistributableValueFactory;
080    import org.codehaus.wadi.web.impl.DummyRouter;
081    import org.codehaus.wadi.web.impl.DummyStatefulHttpServletRequestWrapperPool;
082    import org.codehaus.wadi.web.impl.StandardHttpProxy;
083    import org.codehaus.wadi.web.impl.StandardSessionWrapperFactory;
084    import org.codehaus.wadi.web.impl.WebSessionToSessionPoolAdapter;
085    
086    import EDU.oswego.cs.dl.util.concurrent.Sync;
087    import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
088    
089    /**
090     * 
091     * @version $Rev$ $Date$
092     */
093    public class BasicWADISessionManager implements GBeanLifecycle, SessionManager, WADISessionManager {
094        private static final Log log = LogFactory.getLog(BasicWADISessionManager.class);
095    
096        private final WADISessionManagerConfigInfo configInfo;
097        private final ReplicationManagerFactory repManagerFactory;
098        private final ReplicaStorageFactory repStorageFactory;
099        private final BackingStrategyFactory backingStrategyFactory;
100        private final DispatcherHolder dispatcherHolder;
101        private final CopyOnWriteArrayList listeners;
102    
103        private ClusteredManager manager;
104        private BasicServiceSpace serviceSpace;
105    
106        public BasicWADISessionManager(WADISessionManagerConfigInfo configInfo,
107                ReplicationManagerFactory repManagerFactory, 
108                ReplicaStorageFactory repStorageFactory,
109                BackingStrategyFactory backingStrategyFactory, 
110                DispatcherHolder dispatcherHolder) {
111            this.configInfo = configInfo;
112            this.dispatcherHolder = dispatcherHolder;
113            this.repManagerFactory = repManagerFactory;
114            this.repStorageFactory = repStorageFactory;
115            this.backingStrategyFactory = backingStrategyFactory;
116    
117            listeners = new CopyOnWriteArrayList();
118        }
119    
120        public void doStart() throws Exception {
121            Dispatcher underlyingDisp = dispatcherHolder.getDispatcher();
122    
123            serviceSpace = new BasicServiceSpace(new ServiceSpaceName(configInfo.getServiceSpaceURI()), underlyingDisp);
124    
125            boolean strictOrdering = true;
126            Streamer streamer = new SimpleStreamer();
127            Collapser collapser = new HashingCollapser(1024, 10000);
128            Map mmap = Collections.synchronizedMap(new HashMap());
129            WebSessionPool sessionPool = new SimpleSessionPool(new AtomicallyReplicableSessionFactory());
130    
131            // end of contextualiser stack
132            Contextualiser contextualiser = new DummyContextualiser();
133    
134            // replica aware contextualiser
135             ReplicationManager replicationManager = repManagerFactory.factory(serviceSpace, backingStrategyFactory);
136             ReplicationManager sessionRepManager = new SessionReplicationManager(replicationManager, sessionPool);
137             contextualiser = new ReplicaAwareContextualiser(contextualiser, sessionRepManager);
138    
139            // cluster aware contextualiser
140            contextualiser = new ClusterContextualiser(contextualiser, collapser, new HybridRelocater(5000, 5000, true));
141    
142            contextualiser = new StatelessContextualiser(contextualiser, Pattern.compile("GET|POST", 2), true, 
143                    Pattern.compile(".*\\.(JPG|JPEG|GIF|PNG|ICO|HTML|HTM)", 2), false);
144    
145            // in-memory contextualiser
146            Evicter mevicter = new AbsoluteEvicter(configInfo.getSweepInterval(), strictOrdering,
147                    configInfo.getSessionTimeoutSeconds());
148            SessionPool contextPool = new WebSessionToSessionPoolAdapter(sessionPool);
149            PoolableInvocationWrapperPool requestPool = new DummyStatefulHttpServletRequestWrapperPool();
150            contextualiser = new MotionTracker(contextualiser, mevicter, mmap, streamer, contextPool, requestPool);
151    
152            contextualiser = new SerialContextualiserFrontingMemory(contextualiser, collapser);
153    
154            // Manager
155            manager = new ClusteredManager(sessionPool, 
156                    new DistributableAttributesFactory(), 
157                    new SimpleValuePool(new DistributableValueFactory()), 
158                    new StandardSessionWrapperFactory(), 
159                    null, 
160                    contextualiser,
161                    mmap, 
162                    new DummyRouter(), 
163                    false, 
164                    streamer, 
165                    true,
166                    new ReplicaterAdapterFactory(replicationManager, sessionPool),
167                    new StandardHttpProxy("jsessionid"), 
168                    serviceSpace, 
169                    configInfo.getNumPartitions(), 
170                    collapser);
171    
172            manager.init(new ManagerConfig() {
173                public void callback(Manager manager) {
174                }
175    
176                public ServletContext getServletContext() {
177                    return null;
178                }
179            });
180    
181            ServiceRegistry serviceRegistry = serviceSpace.getServiceRegistry();
182            serviceRegistry.register(ReplicaStorage.NAME, repStorageFactory.factory(serviceSpace));
183            serviceRegistry.register(ReplicationManager.NAME, replicationManager);
184            serviceRegistry.register(ClusteredManager.NAME, manager);
185    
186            serviceSpace.start();
187        }
188    
189        public void doStop() throws Exception {
190            serviceSpace.stop();
191        }
192    
193        public void doFail() {
194            try {
195                serviceSpace.stop();
196            } catch (Exception e) {
197                log.error(e);
198            }
199        }
200    
201        public Session createSession(String sessionId) throws SessionAlreadyExistException {
202            WebSession session;
203            try {
204                session = manager.createWithName(sessionId);
205            } catch (org.codehaus.wadi.SessionAlreadyExistException e) {
206                throw new SessionAlreadyExistException(sessionId);
207            }
208            return new WADISessionAdaptor(session);
209        }
210    
211        public ClusteredManager getManager() {
212            return manager;
213        }
214    
215        public Node getNode() {
216            return dispatcherHolder.getNode();
217        }
218    
219        public void registerListener(SessionListener listener) {
220            listeners.add(listener);
221        }
222    
223        public void unregisterListener(SessionListener listener) {
224            listeners.remove(listener);
225        }
226    
227        private void notifyInboundSessionMigration(WebSession webSession) {
228            for (Iterator iter = listeners.iterator(); iter.hasNext();) {
229                SessionListener listener = (SessionListener) iter.next();
230                listener.notifyInboundSessionMigration(new WADISessionAdaptor(webSession));
231            }
232        }
233    
234        private WebSession notifyOutboundSessionMigration(WebSession webSession) {
235            for (Iterator iter = listeners.iterator(); iter.hasNext();) {
236                SessionListener listener = (SessionListener) iter.next();
237                listener.notifyOutboundSessionMigration(new WADISessionAdaptor(webSession));
238            }
239            return webSession;
240        }
241    
242        private class MotionTracker extends MemoryContextualiser {
243            private final Immoter immoter;
244    
245            private final Emoter emoter;
246    
247            public MotionTracker(Contextualiser next, Evicter evicter, Map map, Streamer streamer, SessionPool pool,
248                    PoolableInvocationWrapperPool requestPool) {
249                super(next, evicter, map, streamer, pool, requestPool);
250    
251                Immoter immoterDelegate = super.getImmoter();
252                immoter = new InboundSessionTracker(immoterDelegate);
253    
254                Emoter emoterDelegate = super.getEmoter();
255                emoter = new OutboundSessionTracker(emoterDelegate);
256            }
257    
258            public Immoter getPromoter(Immoter immoter) {
259                Immoter delegate = super.getPromoter(immoter);
260                if (null == immoter) {
261                    return new InboundSessionTracker(delegate);
262                } else {
263                    return delegate;
264                }
265            }
266    
267            public Immoter getImmoter() {
268                return immoter;
269            }
270    
271            public Emoter getEmoter() {
272                return emoter;
273            }
274        }
275    
276        private class OutboundSessionTracker implements Emoter {
277            private final Emoter delegate;
278    
279            public OutboundSessionTracker(Emoter delegate) {
280                this.delegate = delegate;
281            }
282    
283            public void commit(String arg0, Motable arg1) {
284                notifyOutboundSessionMigration((WebSession) arg1);
285                delegate.commit(arg0, arg1);
286            }
287    
288            public String getInfo() {
289                return delegate.getInfo();
290            }
291    
292            public boolean prepare(String arg0, Motable arg1, Motable arg2) {
293                return delegate.prepare(arg0, arg1, arg2);
294            }
295    
296            public void rollback(String arg0, Motable arg1) {
297                delegate.rollback(arg0, arg1);
298            }
299        }
300    
301        private class InboundSessionTracker implements Immoter {
302            private final Immoter delegate;
303    
304            public InboundSessionTracker(Immoter delegate) {
305                this.delegate = delegate;
306            }
307    
308            public void commit(String arg0, Motable arg1) {
309                notifyInboundSessionMigration((WebSession) arg1);
310                delegate.commit(arg0, arg1);
311            }
312    
313            public boolean contextualise(Invocation arg0, String arg1, Motable arg2, Sync arg3) throws InvocationException {
314                return delegate.contextualise(arg0, arg1, arg2, arg3);
315            }
316    
317            public String getInfo() {
318                return delegate.getInfo();
319            }
320    
321            public Motable nextMotable(String arg0, Motable arg1) {
322                return delegate.nextMotable(arg0, arg1);
323            }
324    
325            public boolean prepare(String arg0, Motable arg1, Motable arg2) {
326                return delegate.prepare(arg0, arg1, arg2);
327            }
328    
329            public void rollback(String arg0, Motable arg1) {
330                delegate.rollback(arg0, arg1);
331            }
332        }
333    
334        public static final GBeanInfo GBEAN_INFO;
335    
336        public static final String GBEAN_ATTR_WADI_CONFIG_INFO = "wadiConfigInfo";
337    
338        public static final String GBEAN_REF_REPLICATION_MANAGER_FACTORY = "ReplicationManagerFactory";
339        public static final String GBEAN_REF_REPLICA_STORAGE_FACTORY = "ReplicaStorageFactory";
340        public static final String GBEAN_REF_BACKING_STRATEGY_FACTORY = "BackingStrategyFactory";
341        public static final String GBEAN_REF_DISPATCHER_HOLDER = "DispatcherHolder";
342    
343        static {
344            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic("WADI Session Manager",
345                    BasicWADISessionManager.class, NameFactory.GERONIMO_SERVICE);
346    
347            infoBuilder.addAttribute(GBEAN_ATTR_WADI_CONFIG_INFO, WADISessionManagerConfigInfo.class, true);
348    
349            infoBuilder.addReference(GBEAN_REF_REPLICATION_MANAGER_FACTORY, ReplicationManagerFactory.class,
350                    NameFactory.GERONIMO_SERVICE);
351            infoBuilder.addReference(GBEAN_REF_REPLICA_STORAGE_FACTORY, ReplicaStorageFactory.class,
352                    NameFactory.GERONIMO_SERVICE);
353            infoBuilder.addReference(GBEAN_REF_BACKING_STRATEGY_FACTORY, BackingStrategyFactory.class,
354                    NameFactory.GERONIMO_SERVICE);
355            infoBuilder.addReference(GBEAN_REF_DISPATCHER_HOLDER, DispatcherHolder.class, NameFactory.GERONIMO_SERVICE);
356    
357            infoBuilder.addInterface(SessionManager.class);
358            infoBuilder.addInterface(WADISessionManager.class);
359    
360            infoBuilder.setConstructor(new String[] { GBEAN_ATTR_WADI_CONFIG_INFO,
361                    GBEAN_REF_REPLICATION_MANAGER_FACTORY, 
362                    GBEAN_REF_REPLICA_STORAGE_FACTORY,
363                    GBEAN_REF_BACKING_STRATEGY_FACTORY, 
364                    GBEAN_REF_DISPATCHER_HOLDER });
365    
366            GBEAN_INFO = infoBuilder.getBeanInfo();
367        }
368    
369        public static GBeanInfo getGBeanInfo() {
370            return GBEAN_INFO;
371        }
372    }