001/*
002 * Copyright (c) 2013 Nu Echo Inc. All rights reserved.
003 */
004
005package com.nuecho.rivr.core.servlet.session;
006
007import javax.servlet.http.*;
008
009import com.nuecho.rivr.core.channel.*;
010import com.nuecho.rivr.core.channel.synchronous.*;
011import com.nuecho.rivr.core.dialogue.*;
012import com.nuecho.rivr.core.servlet.*;
013import com.nuecho.rivr.core.util.*;
014
015/**
016 * Contains everything that is required for the dialogue to run in a
017 * {@link DialogueServlet} controller.
018 * <p>
019 * NOTE: a Rivr {@link Session} can be linked with an {@link HttpSession}. This
020 * can be useful to maintain server stickyness in a clustered environment with
021 * load balancers. Web container generates JSESSIONID cookies for session
022 * tracking purpose but this information is also used by load balancer equipment
023 * between the HTTP user agent and the server to preserve server stickyness.
024 * 
025 * @param <F> type of {@link FirstTurn}
026 * @param <L> type of {@link LastTurn}
027 * @param <O> type of {@link OutputTurn}
028 * @param <I> type of {@link InputTurn}
029 * @param <C> type of {@link DialogueContext}
030 * @see DialogueServlet
031 * @see SessionContainer
032 * @author Nu Echo Inc.
033 */
034public final class Session<I extends InputTurn, O extends OutputTurn, F extends FirstTurn, L extends LastTurn, C extends DialogueContext<I, O>>
035        implements DialogueChannelListener<I, O> {
036    private SynchronousDialogueChannel<I, O, F, L, C> mDialogueChannel;
037
038    private C mDialogueContext;
039
040    private final SessionContainer<I, O, F, L, C> mContainer;
041    private final String mId;
042    private HttpSession mAssociatedHttpSession;
043
044    public Session(SessionContainer<I, O, F, L, C> container, String sessionId) {
045        mContainer = container;
046        mId = sessionId;
047    }
048
049    @Override
050    public void onStart(DialogueChannel<I, O> dialogueChannel) {}
051
052    @Override
053    public void onStop(DialogueChannel<I, O> dialogueChannel) {
054        stop();
055    }
056
057    public synchronized void stop() {
058        if (mDialogueChannel != null && mDialogueChannel.isDialogueActive()) {
059            mDialogueChannel.stop();
060        }
061
062        mContainer.removeSession(mId);
063
064        if (mAssociatedHttpSession != null) {
065            try {
066                mAssociatedHttpSession.invalidate();
067            } catch (IllegalStateException exception) {
068                //already invalidated
069            }
070            mAssociatedHttpSession = null;
071        }
072    }
073
074    public void keepAlive() {
075        if (mAssociatedHttpSession != null) {
076            mAssociatedHttpSession.getAttributeNames();
077        }
078    }
079
080    public String getId() {
081        return mId;
082    }
083
084    public SynchronousDialogueChannel<I, O, F, L, C> getDialogueChannel() {
085        return mDialogueChannel;
086    }
087
088    public void setDialogueChannel(SynchronousDialogueChannel<I, O, F, L, C> dialogueChannel) {
089        mDialogueChannel = dialogueChannel;
090        mDialogueChannel.addListener(this);
091    }
092
093    public C getDialogueContext() {
094        return mDialogueContext;
095    }
096
097    public void setDialogueContext(C dialogueContext) {
098        mDialogueContext = dialogueContext;
099    }
100
101    public void setAssociatedHttpSession(HttpSession associatedHttpSession) {
102        mAssociatedHttpSession = associatedHttpSession;
103    }
104
105    @Override
106    public String toString() {
107        ToStringBuilder builder = new ToStringBuilder(this);
108        builder.appendItem("mDialogueChannel", mDialogueChannel);
109        builder.appendItem("mDialogueContext", mDialogueContext);
110        builder.appendItem("mId", mId);
111        return builder.getString();
112    }
113}