SessionMapConversationContainer.java
package com.github.gfernandez598.swf.conversation.optforrepl;
/*
* #%L
* Spring Web Flow OptForRepl
* %%
* Copyright (C) 2015 gfernandez598
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.springframework.util.Assert;
import org.springframework.webflow.context.ExternalContextHolder;
import org.springframework.webflow.conversation.Conversation;
import org.springframework.webflow.conversation.ConversationId;
import org.springframework.webflow.conversation.ConversationParameters;
import org.springframework.webflow.conversation.NoSuchConversationException;
import org.springframework.webflow.core.collection.SharedAttributeMap;
import java.io.Serializable;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
*
* @author gfernandez598
*
*/
class SessionMapConversationContainer implements Serializable {
private static final long serialVersionUID = 1899010574372604375L;
/**
* Maximum number of conversations in this container. -1 for unlimited.
*/
private int maxConversations;
/**
* The lock timeout in seconds.
*/
private int lockTimeoutSeconds;
/**
* The key of this conversation container in the session.
*/
private String sessionKey;
/**
* The contained conversations. A list of
* {@link org.springframework.webflow.conversation.impl.ContainedConversation}
* objects.
*/
private ConcurrentLinkedQueue<ConversationId> conversations;
/**
* Create a new conversation container.
*
* @param maxConversations
* the maximum number of allowed concurrent conversations, -1 for
* unlimited
* @param lockTimeout
* lock acquisition timeout of conversation in seconds
* @param sessionKey
* the key of this conversation container in the session
*/
public SessionMapConversationContainer(int maxConversations,
int lockTimeout, String sessionKey) {
Assert.hasText(sessionKey, "A sessionKey must be supplied.");
this.maxConversations = maxConversations;
this.lockTimeoutSeconds = lockTimeout;
this.sessionKey = sessionKey;
this.conversations = new ConcurrentLinkedQueue<ConversationId>();
}
/**
*
* @return the lock timeout in seconds.
*/
int getLockTimeoutSeconds() {
return lockTimeoutSeconds;
}
/**
* Returns the key of this conversation container in the session. For
* package level use only.
*/
String getSessionKey() {
return sessionKey;
}
/**
* Returns the current size of the conversation container: the number of
* conversations contained within it.
*/
public int size() {
return conversations.size();
}
/**
* Create a new conversation based on given parameters and add it to the
* container.
*
* @param id
* the unique id of the conversation
* @param parameters
* descriptive parameters
* @return the created conversation
*/
public synchronized Conversation createAndAddConversation(
ConversationId id, ConversationParameters parameters) {
// add the conversation to the session map also
ContainedConversation conversation;
final SharedAttributeMap sessionMap = ExternalContextHolder
.getExternalContext().getSessionMap();
synchronized (sessionMap.getMutex()) {
// add the new conversation to the queue
conversation = (ContainedConversation) sessionMap
.get(getConversationKey(id));
if (conversation == null) {
conversations.add(id);
conversation = new ContainedConversation(this, id);
conversation.putAttribute("name", parameters.getName());
conversation.putAttribute("caption", parameters.getCaption());
conversation.putAttribute("description",
parameters.getDescription());
sessionMap.put(getConversationKey(id), conversation);
}
}
if (maxExceeded()) {
// end oldest conversation by getting the first one out of the FIFO
// queue
final ConversationId oldestId = conversations.poll();
final Conversation conver = getConversation(oldestId);
if (conver != null) {
conver.end();
removeConversation(oldestId);
}
}
return conversation;
}
/**
* Return the identified conversation.
*
* @param id
* the id to lookup
* @return the conversation
* @throws org.springframework.webflow.conversation.NoSuchConversationException
* if the conversation cannot be found
*/
public synchronized Conversation getConversation(ConversationId id)
throws NoSuchConversationException {
SharedAttributeMap sessionMap = ExternalContextHolder
.getExternalContext().getSessionMap();
synchronized (sessionMap.getMutex()) {
ContainedConversation conversation = (ContainedConversation) sessionMap
.get(getConversationKey(id));
if (conversation != null) {
return conversation;
}
}
throw new NoSuchConversationException(id);
}
/**
* Save the conversation back to the session. We need to do this for
* replication
*
* @param id
*/
public synchronized void saveConversation(ConversationId id) {
final SharedAttributeMap sessionMap = ExternalContextHolder
.getExternalContext().getSessionMap();
// remove from the list of conversations
synchronized (sessionMap.getMutex()) {
final ContainedConversation conversation = (ContainedConversation) sessionMap
.get(getConversationKey(id));
if (conversation != null) {
sessionMap.put(getConversationKey(id), conversation);
}
}
}
/**
* Remove identified conversation from this container.
*/
public synchronized void removeConversation(ConversationId id) {
SharedAttributeMap sessionMap = ExternalContextHolder
.getExternalContext().getSessionMap();
// remove from the list of conversations
synchronized (sessionMap.getMutex()) {
conversations.remove(id);
sessionMap.remove(getConversationKey(id));
}
}
/**
* Has the maximum number of allowed concurrent conversations in the session
* been exceeded?
*/
protected boolean maxExceeded() {
return maxConversations > 0 && conversations.size() > maxConversations;
}
/**
* Get the conversaion session key. Package use only.
*
* @param id
* @return the key
*/
String getConversationKey(ConversationId id) {
Assert.notNull(id, "conversationId is required.");
return getSessionKey() + ".conversation." + id;
}
}