/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.conversation;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.Conversation;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.WindowContextConfig;
import org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.conversation.spi.EditableConversation;
import org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.conversation.spi.EditableWindowContext;
import org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.conversation.spi.ConversationKey;
import org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.conversation.spi.ConversationFactory;
import org.apache.myfaces.extensions.cdi.javaee.jsf.impl.scope.conversation.spi.JsfAwareWindowContextConfig;
import org.apache.myfaces.extensions.cdi.javaee.jsf.impl.util.RequestCache;
import org.apache.myfaces.extensions.cdi.javaee.jsf.impl.util.JsfUtils;
import static org.apache.myfaces.extensions.cdi.javaee.jsf.impl.util.ExceptionUtils.conversationNotEditableException;
import javax.enterprise.inject.Typed;
import java.util.Date;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.lang.annotation.Annotation;
/**
* TODO
*
* @author Gerhard Petracek
*/
@Typed()
public class JsfWindowContext implements EditableWindowContext
{
private static final long serialVersionUID = 5272798129165017829L;
private final String id;
private final JsfAwareWindowContextConfig jsfAwareWindowContextConfig;
private final boolean projectStageDevelopment;
private Map<ConversationKey, EditableConversation> groupedConversations
= new ConcurrentHashMap<ConversationKey, EditableConversation>();
private Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
private final TimeoutExpirationEvaluator expirationEvaluator;
protected JsfWindowContext(String windowContextId,
JsfAwareWindowContextConfig jsfAwareWindowContextConfig,
boolean projectStageDevelopment)
{
this.id = windowContextId;
this.jsfAwareWindowContextConfig = jsfAwareWindowContextConfig;
this.expirationEvaluator = new TimeoutExpirationEvaluator(
this.jsfAwareWindowContextConfig.getWindowContextTimeoutInMinutes());
this.projectStageDevelopment = projectStageDevelopment;
}
public String getId()
{
return this.id;
}
public void endConversations()
{
endConversations(false);
}
public void end()
{
endConversations(true);
this.attributes.clear();
}
public synchronized void endConversations(boolean forceEnd)
{
for (Map.Entry<ConversationKey, EditableConversation> conversationEntry : this.groupedConversations.entrySet())
{
endAndRemoveConversation(conversationEntry.getKey(), conversationEntry.getValue(), forceEnd);
}
JsfUtils.resetConversationCache();
}
public EditableConversation getConversation(Class conversationGroupKey, Annotation... qualifiers)
{
ConversationKey conversationKey =
new DefaultConversationKey(conversationGroupKey, this.projectStageDevelopment, qualifiers);
EditableConversation conversation = RequestCache.getConversation(conversationKey);
if(conversation == null)
{
conversation = this.groupedConversations.get(conversationKey);
//TODO
if (conversation != null && !conversation.isActive())
{
endAndRemoveConversation(conversationKey, conversation, true);
conversation = null;
}
if (conversation == null)
{
conversation = createConversation(conversationGroupKey, qualifiers);
this.groupedConversations.put(conversationKey, conversation);
}
RequestCache.setConversation(conversationKey, conversation);
}
return conversation;
}
public Conversation endConversation(Class conversationGroupKey, Annotation... qualifiers)
{
ConversationKey conversationKey =
new DefaultConversationKey(conversationGroupKey, this.projectStageDevelopment, qualifiers);
Conversation conversation = this.groupedConversations.get(conversationKey);
if(!(conversation instanceof EditableConversation))
{
throw conversationNotEditableException(conversation);
}
return endAndRemoveConversation(conversationKey, (EditableConversation)conversation, true);
}
public Set<Conversation> endConversationGroup(Class conversationGroupKey)
{
Set<Conversation> removedConversations = new HashSet<Conversation>();
for(Map.Entry<ConversationKey, EditableConversation> conversationEntry : this.groupedConversations.entrySet())
{
if(conversationGroupKey.isAssignableFrom(conversationEntry.getKey().getConversationGroup()))
{
removedConversations.add(
endAndRemoveConversation(conversationEntry.getKey(), conversationEntry.getValue(), true));
}
}
return removedConversations;
}
private EditableConversation endAndRemoveConversation(ConversationKey conversationKey,
EditableConversation conversation,
boolean forceEnd)
{
if (forceEnd)
{
conversation.end();
return this.groupedConversations.remove(conversationKey);
}
else if(conversation instanceof EditableConversation)
{
conversation.deactivate();
if(!conversation.isActive())
{
conversation.end();
return this.groupedConversations.remove(conversationKey);
}
}
return null;
}
public EditableConversation createConversation(Class conversationGroupKey, Annotation... qualifiers)
{
ConversationKey conversationKey =
new DefaultConversationKey(conversationGroupKey, this.projectStageDevelopment, qualifiers);
ConversationFactory conversationFactory = this.jsfAwareWindowContextConfig.getConversationFactory();
return conversationFactory.createConversation(conversationKey, this.jsfAwareWindowContextConfig);
}
public Map<ConversationKey /*conversation group*/, EditableConversation> getConversations()
{
return Collections.unmodifiableMap(this.groupedConversations);
}
public WindowContextConfig getConfig()
{
return this.jsfAwareWindowContextConfig;
}
public boolean isActive()
{
return !this.expirationEvaluator.isExpired();
}
public Date getLastAccess()
{
return this.expirationEvaluator.getLastAccess();
}
public void touch()
{
this.expirationEvaluator.touch();
}
public void removeInactiveConversations()
{
Iterator<EditableConversation> conversations = this.groupedConversations.values().iterator();
EditableConversation conversation;
while (conversations.hasNext())
{
conversation = conversations.next();
if (!conversation.getActiveState())
{
conversations.remove();
}
}
}
public boolean setAttribute(String name, Object value)
{
return setAttribute(name, value, true);
}
public boolean setAttribute(String name, Object value, boolean forceOverride)
{
if(value == null || (!forceOverride && containsAttribute(name)))
{
return false;
}
this.attributes.put(name, value);
return true;
}
public boolean containsAttribute(String name)
{
return this.attributes.containsKey(name);
}
public <T> T getAttribute(String name, Class<T> targetType)
{
//noinspection unchecked
return (T)this.attributes.get(name);
}
}