/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.
*/
package org.jboss.weld.contexts.beanstore;
import static org.jboss.weld.util.reflection.Reflections.cast;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.jboss.weld.context.api.ContextualInstance;
import org.jboss.weld.logging.ContextLogger;
import org.jboss.weld.serialization.spi.BeanIdentifier;
/**
* <p>
* A bound bean store backed by attributes. This bean store is "write-through" -
* if attached it will write any modifications to the backing store immediately.
* If detached modifications will not be written through. If the bean store is
* reattached, then any local modifications will be written to the underlying
* store.
* </p>
* <p/>
* <p>
* This construct is not thread safe.
* </p>
*
* @author Pete Muir
* @author Nicklas Karlsson
* @author David Allen
*/
public abstract class AttributeBeanStore implements BoundBeanStore {
private final HashMapBeanStore beanStore;
private final NamingScheme namingScheme;
private final boolean attributeLazyFetchingEnabled;
private boolean attached;
/**
*
* @param namingScheme
* @param attributeLazyFetchingEnabled
*/
public AttributeBeanStore(NamingScheme namingScheme, boolean attributeLazyFetchingEnabled) {
this.namingScheme = namingScheme;
this.beanStore = new HashMapBeanStore();
this.attributeLazyFetchingEnabled = attributeLazyFetchingEnabled;
}
/**
* Detach the bean store, causing updates to longer be written through to the
* underlying store.
*/
public boolean detach() {
if (attached) {
attached = false;
ContextLogger.LOG.beanStoreDetached(this);
return true;
} else {
return false;
}
}
/**
* <p>
* Attach the bean store, any updates from now on will be written through to
* the underlying store.
* </p>
* <p/>
* <p>
* When the bean store is attached, the detached state is assumed to be
* authoritative if there are any conflicts.
* </p>
*/
public boolean attach() {
if (!attached) {
attached = true;
if (isLocalBeanStoreSyncNeeded()) {
if (!beanStore.delegate().isEmpty()) {
// The local bean store is authoritative, so copy everything to the backing store
for (BeanIdentifier id : beanStore) {
ContextualInstance<?> instance = beanStore.get(id);
String prefixedId = getNamingScheme().prefix(id);
ContextLogger.LOG.updatingStoreWithContextualUnderId(instance, id);
setAttribute(prefixedId, instance);
}
}
if (!isAttributeLazyFetchingEnabled()) {
// Additionally copy anything not in the local bean store but in the backing store
fetchUninitializedAttributes();
}
}
return true;
} else {
return false;
}
}
/**
* Fetch all relevant attributes from the backing store and copy instances which are not present in the local bean store.
*/
public void fetchUninitializedAttributes() {
for (String prefixedId : getPrefixedAttributeNames()) {
BeanIdentifier id = getNamingScheme().deprefix(prefixedId);
if (!beanStore.contains(id)) {
ContextualInstance<?> instance = (ContextualInstance<?>) getAttribute(prefixedId);
beanStore.put(id, instance);
ContextLogger.LOG.addingDetachedContextualUnderId(instance, id);
}
}
}
public boolean isAttached() {
return attached;
}
@Override
public <T> ContextualInstance<T> get(BeanIdentifier id) {
ContextualInstance<T> instance = beanStore.get(id);
if(instance == null && isAttached() && isAttributeLazyFetchingEnabled()) {
instance = cast(getAttribute(namingScheme.prefix(id)));
if(instance != null) {
beanStore.put(id, instance);
}
}
ContextLogger.LOG.contextualInstanceFound(id, instance, this);
return instance;
}
@Override
public <T> void put(BeanIdentifier id, ContextualInstance<T> instance) {
beanStore.put(id, instance); // moved due to WELD-892
if (isAttached()) {
setAttribute(namingScheme.prefix(id), instance);
}
ContextLogger.LOG.contextualInstanceAdded(instance.getContextual(), id, this);
}
@Override
public <T> ContextualInstance<T> remove(BeanIdentifier id) {
ContextualInstance<T> instance = beanStore.remove(id);
if (instance != null) {
if (isAttached()) {
removeAttribute(namingScheme.prefix(id));
}
ContextLogger.LOG.contextualInstanceRemoved(id, this);
}
return instance;
}
public void clear() {
Iterator<BeanIdentifier> it = iterator();
while (it.hasNext()) {
BeanIdentifier id = it.next();
if (isAttached()) {
String prefixedId = namingScheme.prefix(id);
removeAttribute(prefixedId);
}
it.remove();
ContextLogger.LOG.contextualInstanceRemoved(id, this);
}
ContextLogger.LOG.contextCleared(this);
}
public boolean contains(BeanIdentifier id) {
return get(id) != null;
}
protected NamingScheme getNamingScheme() {
return namingScheme;
}
public Iterator<BeanIdentifier> iterator() {
Iterator<BeanIdentifier> iterator;
if (isAttributeLazyFetchingEnabled()) {
// Merge the bean identifiers from the local bean store and the backing store
Set<BeanIdentifier> identifiers = new HashSet<>();
for (BeanIdentifier id : beanStore) {
identifiers.add(id);
}
for (String prefixedId : getPrefixedAttributeNames()) {
identifiers.add(getNamingScheme().deprefix(prefixedId));
}
iterator = identifiers.iterator();
} else {
iterator = beanStore.iterator();
}
return iterator;
}
/**
* Gets an attribute from the underlying storage
*
* @param prefixedId The (prefixed) id of the attribute
* @return The data
*/
protected abstract Object getAttribute(String prefixedId);
/**
* Removes an attribute from the underlying storage
*
* @param prefixedId The (prefixed) id of the attribute to remove
*/
protected abstract void removeAttribute(String prefixedId);
/**
* Gets an enumeration of the attribute names present in the underlying
* storage. The collection must guarantee non-interference with other threads
* when iterating over it using iterator.
*
* @return The attribute names
*/
protected abstract Iterator<String> getAttributeNames();
/**
* Gets an enumeration of the attribute names present in the underlying
* storage
*
* @return The attribute names
*/
protected Collection<String> getPrefixedAttributeNames() {
return getNamingScheme().filterIds(getAttributeNames());
}
/**
* Sets an instance under a key in the underlying storage
*
* @param prefixedId The (prefixed) id of the attribute to set
* @param instance The instance
*/
protected abstract void setAttribute(String prefixedId, Object instance);
public LockedBean lock(final BeanIdentifier id) {
LockStore lockStore = getLockStore();
if(lockStore == null) {
//if the lockstore is null then no locking is necessary, as the underlying
//context is single threaded
return null;
}
return lockStore.lock(id);
}
protected abstract LockStore getLockStore();
/**
*
* @return <code>true</code> if a bean store synchronization is required during {@link #attach()} invocation, <code>false</code> otherwise
*/
protected boolean isLocalBeanStoreSyncNeeded() {
return true;
}
/**
*
* @return <code>true</code> if attributes should be fetched lazily, <code>false</code> otherwise
*/
public boolean isAttributeLazyFetchingEnabled() {
return attributeLazyFetchingEnabled;
}
}