/* * JBoss, Home of Professional Open Source * Copyright 2012, 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; import java.io.Serializable; import javax.enterprise.context.spi.Contextual; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.PassivationCapable; import org.jboss.weld.Container; import org.jboss.weld.bean.ForwardingBean; import org.jboss.weld.bean.WrappedContextual; import org.jboss.weld.serialization.BeanIdentifierIndex; import org.jboss.weld.serialization.spi.BeanIdentifier; import org.jboss.weld.serialization.spi.ContextualStore; import org.jboss.weld.serialization.spi.helpers.SerializableContextual; import org.jboss.weld.util.Beans; import org.jboss.weld.util.reflection.Reflections; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Produces wrappers for {@link Contextual}s which are serializable. * * @author Jozef Hartinger * @author Martin Kouba */ public class SerializableContextualFactory { private SerializableContextualFactory() { } @SuppressWarnings({ "rawtypes", "unchecked" }) public static <C extends Contextual<I>, I> SerializableContextual<C, I> create(String contextId, C contextual, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) { if (contextual instanceof Bean) { if (contextual instanceof PassivationCapable) { return new PassivationCapableSerializableBean(contextId, Reflections.<Bean> cast(contextual), contextualStore, beanIdentifierIndex); } else { return new DefaultSerializableBean(contextId, Reflections.<Bean> cast(contextual), contextualStore, beanIdentifierIndex); } } else { if (contextual instanceof PassivationCapable) { return new PassivationCapableSerializableContextual(contextId, contextual, contextualStore, beanIdentifierIndex); } else { return new DefaultSerializableContextual<C, I>(contextId, contextual, contextualStore, beanIdentifierIndex); } } } private static final class SerializableContextualHolder<C extends Contextual<I>, I> implements Serializable { private static final long serialVersionUID = 46941665668478370L; @SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "A cache which is lazily loaded") // A cached, transient version of the contextual private transient C cached; // Only one of the three fields is used at the same time - directly serializable contextual, bean identifier or index private final C serializable; private final BeanIdentifier identifier; private final Integer identifierIndex; private final String contextId; private transient ContextualStore cachedContextualStore; private transient BeanIdentifierIndex beanIdentifierIndex; SerializableContextualHolder(String contextId, C contextual, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) { this.contextId = contextId; this.cachedContextualStore = contextualStore; if (contextual instanceof Serializable) { // the contextual is serializable, so we can just use it this.serializable = contextual; this.identifier = null; this.identifierIndex = null; } else { this.serializable = null; BeanIdentifier beanIdentifier = getId(contextual, contextualStore); // The index may be null or not built yet Integer idx = null; if (beanIdentifierIndex != null && beanIdentifierIndex.isBuilt()) { idx = beanIdentifierIndex.getIndex(beanIdentifier); } if (idx != null) { this.identifierIndex = idx; this.identifier = null; } else { this.identifierIndex = null; this.identifier = beanIdentifier; } } // cache the contextual this.cached = contextual; } protected BeanIdentifier getId(C contextual, ContextualStore contextualStore) { return Beans.getIdentifier(contextual, contextualStore); } protected ContextualStore getContextualStore() { if (cachedContextualStore == null) { this.cachedContextualStore = Container.instance(contextId).services().get(ContextualStore.class); } return this.cachedContextualStore; } protected BeanIdentifierIndex getBeanIdentifierIndex() { if (beanIdentifierIndex == null) { beanIdentifierIndex = Container.instance(contextId).services().get(BeanIdentifierIndex.class); } return beanIdentifierIndex; } protected C get() { if (cached == null) { loadContextual(); } return cached; } private void loadContextual() { if (serializable != null) { cached = serializable; } else if (identifierIndex != null) { cached = getContextualStore().<C, I> getContextual(getBeanIdentifierIndex().getIdentifier(identifierIndex)); } else if (identifier != null) { cached = getContextualStore().<C, I> getContextual(identifier); } if (cached == null) { throw new IllegalStateException("Error restoring serialized contextual with id " + identifier); } } } private abstract static class AbstractSerializableBean<B extends Bean<I>, I> extends ForwardingBean<I> implements SerializableContextual<B, I>, WrappedContextual<I> { private static final long serialVersionUID = 7594992948498685840L; private final SerializableContextualHolder<B, I> holder; AbstractSerializableBean(String contextId, B bean, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) { this.holder = new SerializableContextualHolder<B, I>(contextId, bean, contextualStore, beanIdentifierIndex); } @Override public B get() { return holder.get(); } @Override public Bean<I> delegate() { return get(); } @Override public boolean equals(Object obj) { // if the arriving object is also a AbstractSerializableBean, then unwrap it if (obj instanceof AbstractSerializableBean<?, ?>) { return delegate().equals(((AbstractSerializableBean<?, ?>) obj).get()); } else { return delegate().equals(obj); } } @Override public int hashCode() { return delegate().hashCode(); } } private abstract static class AbstractSerializableContextual<C extends Contextual<I>, I> extends ForwardingContextual<I> implements SerializableContextual<C, I>, WrappedContextual<I> { private static final long serialVersionUID = 107855630671709443L; private final SerializableContextualHolder<C, I> holder; AbstractSerializableContextual(String contextId, C contextual, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) { this.holder = new SerializableContextualHolder<C, I>(contextId, contextual, contextualStore, beanIdentifierIndex); } @Override public Contextual<I> delegate() { return get(); } public C get() { return holder.get(); } @Override public boolean equals(Object obj) { // if the arriving object is also a AbstractSerializableContextual, then unwrap it if (obj instanceof AbstractSerializableContextual<?, ?>) { return delegate().equals(((AbstractSerializableContextual<?, ?>) obj).get()); } else { return delegate().equals(obj); } } @Override public int hashCode() { return delegate().hashCode(); } } // for Contextuals that are not PassivationCapable - bean id is generated (may not be portable between container instances) private static class DefaultSerializableContextual<C extends Contextual<I>, I> extends AbstractSerializableContextual<C, I> { private static final long serialVersionUID = -5102624795925717767L; public DefaultSerializableContextual(String contextId, C contextual, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) { super(contextId, contextual, contextualStore, beanIdentifierIndex); } } // every Contextual with passivating scope should implement PassivationCapable private static class PassivationCapableSerializableContextual<C extends Contextual<I> & PassivationCapable, I> extends AbstractSerializableContextual<C, I> implements PassivationCapable { private static final long serialVersionUID = -2753893863961869301L; public PassivationCapableSerializableContextual(String contextId, C contextual, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) { super(contextId, contextual, contextualStore, beanIdentifierIndex); } @Override public String getId() { return get().getId(); } } private static class DefaultSerializableBean<B extends Bean<I>, I> extends AbstractSerializableBean<B, I> { private static final long serialVersionUID = -8901252027789701049L; public DefaultSerializableBean(String contextId, B bean, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) { super(contextId, bean, contextualStore, beanIdentifierIndex); } } private static class PassivationCapableSerializableBean<B extends Bean<I> & PassivationCapable, I> extends AbstractSerializableBean<B, I> implements PassivationCapable { private static final long serialVersionUID = 7458443513156329183L; public PassivationCapableSerializableBean(String contextId, B bean, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) { super(contextId, bean, contextualStore, beanIdentifierIndex); } @Override public String getId() { return get().getId(); } } }