001    package org.tynamo.conversations.services;
002    
003    import java.util.Collections;
004    import java.util.HashMap;
005    import java.util.Iterator;
006    import java.util.Map;
007    
008    import org.apache.tapestry5.EventContext;
009    import org.apache.tapestry5.services.ComponentEventRequestParameters;
010    import org.apache.tapestry5.services.Cookies;
011    import org.apache.tapestry5.services.PageRenderRequestParameters;
012    import org.apache.tapestry5.services.Request;
013    
014    public class ConversationManagerImpl implements ConversationManager {
015            // protected so you can override toString() in case user application already uses the same keys
016            // for something else
017            protected enum Keys {
018                    _conversationId, conversations
019            };
020    
021            private final Request request;
022    
023            private final Cookies cookies;
024    
025            private ConversationalPersistentFieldStrategy pagePersistentFieldStrategy;
026    
027            public ConversationManagerImpl(Request request, Cookies cookies) {
028                    this.request = request;
029                    this.cookies = cookies;
030            }
031    
032            @SuppressWarnings("unchecked")
033            protected Map<String, Conversation> getConversations() {
034                    Map<String, Conversation> conversations = (Map<String, Conversation>) request.getSession(true).getAttribute(Keys.conversations.toString());
035                    if (conversations == null) {
036                            conversations = Collections.synchronizedMap(new HashMap<String, Conversation>());
037                            request.getSession(true).setAttribute(Keys.conversations.toString(), conversations);
038                    }
039                    return conversations;
040            }
041    
042            public boolean activateConversation(Object parameterObject) {
043                    if (parameterObject == null) return false;
044                    EventContext activationContext = null;
045                    String pageName = null;
046                    if (parameterObject instanceof PageRenderRequestParameters) {
047                            activationContext = ((PageRenderRequestParameters) parameterObject).getActivationContext();
048                            pageName = ((PageRenderRequestParameters) parameterObject).getLogicalPageName();
049                    } else if (parameterObject instanceof ComponentEventRequestParameters) {
050                            activationContext = ((ComponentEventRequestParameters) parameterObject).getPageActivationContext();
051                            pageName = ((ComponentEventRequestParameters) parameterObject).getActivePageName();
052                    }
053    
054                    String conversationId = null;
055    
056                    // Try reading the conversation id from a cookie first
057                    try {
058                            conversationId = cookies.readCookieValue(pageName + ConversationManagerImpl.Keys._conversationId);
059                            Conversation conversation = getConversations().get(conversationId);
060                            if (conversation == null) conversationId = null;
061                            else if (!conversation.isUsingCookie()) conversationId = null;
062                    } catch (NumberFormatException e) {
063                            // Ignore
064                    }
065                    // If cookie-based conversation isn't available, try activation context
066                    if (conversationId == null) if (activationContext != null) try {
067                            conversationId = activationContext.get(String.class, activationContext.getCount() - 1);
068                    } catch (RuntimeException e) {
069                            // Ignore
070                    }
071    
072                    return activate(endConversationIfIdle(conversationId));
073            }
074    
075            private boolean activate(Conversation conversation) {
076                    if (conversation == null) return false;
077                    request.setAttribute(Keys._conversationId.toString(), conversation.getId());
078                    return true;
079            }
080    
081            public String createConversation(String pageName, Integer maxIdleSeconds) {
082                    return createConversation(pageName, maxIdleSeconds, false);
083            }
084    
085            public String createConversation(String pageName, Integer maxIdleSeconds, boolean useCookie) {
086                    return createConversation(String.valueOf(System.currentTimeMillis()), pageName, maxIdleSeconds, 0, useCookie);
087            }
088    
089            public String createConversation(String pageName, Integer maxIdleSeconds, Integer maxConversationLengthSeconds, boolean useCookie) {
090                    return createConversation(String.valueOf(System.currentTimeMillis()), pageName, maxIdleSeconds, maxConversationLengthSeconds, useCookie);
091            }
092    
093            public String createConversation(String id, String pageName, Integer maxIdleSeconds, Integer maxConversationLengthSeconds, boolean useCookie) {
094                    pageName = pageName == null ? "" : pageName.toLowerCase();
095                    // Don't use path in a cookie, it's actually relatively difficult to find out from here
096                    if (useCookie) cookies.writeCookieValue(pageName + Keys._conversationId.toString(), String.valueOf(id));
097                    Conversation conversation = new Conversation(id, pageName, maxIdleSeconds, maxConversationLengthSeconds, useCookie);
098                    endIdleConversations();
099                    getConversations().put(id, conversation);
100                    activate(conversation);
101                    return id;
102            }
103    
104            public void endIdleConversations() {
105                    Iterator<Conversation> iterator = getConversations().values().iterator();
106                    while (iterator.hasNext()) {
107                            Conversation conversation = iterator.next();
108                            if (conversation.isIdle(false)) {
109                                    discardConversation(conversation);
110                                    iterator.remove();
111                            }
112                    }
113            }
114    
115            protected void discardConversation(Conversation conversation) {
116                    if (conversation == null) return;
117                    if (conversation.isUsingCookie()) cookies.removeCookieValue(String.valueOf(conversation.getId()));
118                    if (pagePersistentFieldStrategy != null) pagePersistentFieldStrategy.discardChanges(conversation.getPageName());
119            }
120    
121            public boolean exists(String conversationId) {
122                    Conversation conversation = getConversations().get(conversationId);
123                    if (conversation == null) return false;
124                    else return true;
125            }
126    
127            protected Conversation endConversationIfIdle(String conversationId) {
128                    Conversation conversation = getConversations().get(conversationId);
129                    if (conversation == null) return null;
130                    boolean resetTimeout = !("false".equals(request.getParameter(Parameters.keepalive.name())));
131                    if (conversation.isIdle(resetTimeout)) {
132                            discardConversation(conversation);
133                            getConversations().remove(conversation.getId());
134                            conversationId = null;
135                    }
136                    return conversation;
137            }
138    
139            public String getActiveConversation() {
140                    String conversationId = (String) request.getAttribute(Keys._conversationId.toString());
141                    if (conversationId == null) return null;
142                    return exists(conversationId) ? conversationId : null;
143            }
144    
145            public int getSecondsBeforeActiveConversationBecomesIdle() {
146                    String conversationId = getActiveConversation();
147                    if (conversationId == null) return -1;
148                    Conversation conversation = getConversations().get(conversationId);
149                    if (conversation == null) return -1;
150                    return conversation.getSecondsBeforeBecomesIdle();
151            }
152    
153            public void setPagePersistentFieldStrategy(ConversationalPersistentFieldStrategy pagePersistentFieldStrategy) {
154                    this.pagePersistentFieldStrategy = pagePersistentFieldStrategy;
155            }
156    
157            public boolean isActiveConversation(String conversationId) {
158                    if (conversationId == null) return false;
159                    return conversationId.equals(getActiveConversation());
160            }
161    
162            public String endConversation(String conversationId) {
163                    Conversation conversation = getConversations().get(conversationId);
164                    if (conversation == null) return null;
165                    discardConversation(conversation);
166                    getConversations().remove(conversation.getId());
167                    return null;
168            }
169    }