/*
* JBoss, Home of Professional Open Source.
* Copyright 2015, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.wildfly.clustering.web.infinispan.session.fine;
import java.util.AbstractMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.infinispan.Cache;
import org.infinispan.context.Flag;
import org.wildfly.clustering.ee.Mutator;
import org.wildfly.clustering.ee.infinispan.CacheEntryMutator;
import org.wildfly.clustering.ee.infinispan.CacheProperties;
import org.wildfly.clustering.marshalling.spi.InvalidSerializedFormException;
import org.wildfly.clustering.marshalling.spi.Marshaller;
import org.wildfly.clustering.web.infinispan.logging.InfinispanWebLogger;
import org.wildfly.clustering.web.infinispan.session.SessionAttributes;
import org.wildfly.clustering.web.infinispan.session.SessionAttributesFactory;
import org.wildfly.clustering.web.session.ImmutableSessionAttributes;
/**
* {@link SessionAttributesFactory} for fine granularity sessions.
* A given session's attributes are mapped to N+1 co-located cache entries, where N is the number of session attributes.
* A separate cache entry stores the activate attribute names for the session.
* @author Paul Ferraro
*/
public class FineSessionAttributesFactory<V> implements SessionAttributesFactory<SessionAttributeNamesEntry> {
private final Cache<SessionAttributeNamesKey, SessionAttributeNamesEntry> namesCache;
private final Cache<SessionAttributeKey, V> attributeCache;
private final Marshaller<Object, V> marshaller;
private final CacheProperties properties;
public FineSessionAttributesFactory(Cache<SessionAttributeNamesKey, SessionAttributeNamesEntry> namesCache, Cache<SessionAttributeKey, V> attributeCache, Marshaller<Object, V> marshaller, CacheProperties properties) {
this.namesCache = namesCache;
this.attributeCache = attributeCache;
this.marshaller = marshaller;
this.properties = properties;
}
@Override
public SessionAttributeNamesEntry createValue(String id, Void context) {
SessionAttributeNamesEntry entry = new SessionAttributeNamesEntry(new AtomicInteger(), new ConcurrentHashMap<>());
this.namesCache.getAdvancedCache().withFlags(Flag.IGNORE_RETURN_VALUES).put(new SessionAttributeNamesKey(id), entry);
return entry;
}
@Override
public SessionAttributeNamesEntry findValue(String id) {
SessionAttributeNamesEntry entry = this.namesCache.get(new SessionAttributeNamesKey(id));
if (entry != null) {
ConcurrentMap<String, Integer> names = entry.getNames();
Map<SessionAttributeKey, V> attributes = this.attributeCache.getAdvancedCache().getAll(names.values().stream().map(attributeId -> new SessionAttributeKey(id, attributeId)).collect(Collectors.toSet()));
Predicate<Map.Entry<String, V>> invalidAttribute = attribute -> {
V value = attribute.getValue();
if (value == null) {
InfinispanWebLogger.ROOT_LOGGER.missingSessionAttributeCacheEntry(id, attribute.getKey());
return true;
}
try {
this.marshaller.read(attribute.getValue());
return false;
} catch (InvalidSerializedFormException e) {
InfinispanWebLogger.ROOT_LOGGER.failedToActivateSessionAttribute(e, id, attribute.getKey());
return true;
}
};
if (names.entrySet().stream().map(name -> new AbstractMap.SimpleImmutableEntry<>(name.getKey(), attributes.get(new SessionAttributeKey(id, name.getValue())))).anyMatch(invalidAttribute)) {
// If any attributes are invalid - remove them all
this.remove(id);
return null;
}
}
return entry;
}
@Override
public boolean remove(String id) {
SessionAttributeNamesEntry entry = this.namesCache.getAdvancedCache().withFlags(Flag.FORCE_SYNCHRONOUS).remove(new SessionAttributeNamesKey(id));
if (entry == null) return false;
entry.getNames().values().forEach(attributeId -> this.attributeCache.getAdvancedCache().withFlags(Flag.IGNORE_RETURN_VALUES).remove(new SessionAttributeKey(id, attributeId)));
return true;
}
@Override
public boolean evict(String id) {
SessionAttributeNamesKey key = new SessionAttributeNamesKey(id);
SessionAttributeNamesEntry entry = this.namesCache.getAdvancedCache().withFlags(EVICTION_FLAGS).get(key);
if (entry != null) {
entry.getNames().entrySet().stream().forEach(attribute -> {
try {
this.attributeCache.evict(new SessionAttributeKey(id, attribute.getValue()));
} catch (Throwable e) {
InfinispanWebLogger.ROOT_LOGGER.failedToPassivateSessionAttribute(e, id, attribute.getKey());
}
});
this.namesCache.getAdvancedCache().withFlags(Flag.FAIL_SILENTLY).evict(key);
}
return (entry != null);
}
@Override
public SessionAttributes createSessionAttributes(String id, SessionAttributeNamesEntry entry) {
SessionAttributeNamesKey key = new SessionAttributeNamesKey(id);
Mutator mutator = this.properties.isTransactional() && this.namesCache.getAdvancedCache().getCacheEntry(key).isCreated() ? Mutator.PASSIVE : new CacheEntryMutator<>(this.namesCache, key, entry);
return new FineSessionAttributes<>(id, entry.getSequence(), entry.getNames(), mutator, this.attributeCache, this.marshaller, this.properties);
}
@Override
public ImmutableSessionAttributes createImmutableSessionAttributes(String id, SessionAttributeNamesEntry entry) {
return new FineImmutableSessionAttributes<>(id, entry.getNames(), this.attributeCache, this.marshaller);
}
}