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 }