All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.weld.context.SerializableContextualFactory Maven / Gradle / Ivy

There is a newer version: 3.0.0.Alpha1
Show newest version
/*
 * 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.context;

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.reflection.Reflections;

import edu.umd.cs.findbugs.annotations.SuppressWarnings;

/**
 * Produces wrappers for {@link Contextual}s which are serializable.
 *
 * @author Jozef Hartinger
 * @author Martin Kouba
 */
public class SerializableContextualFactory {

    private SerializableContextualFactory() {
    }

    @java.lang.SuppressWarnings({ "rawtypes", "unchecked" })
    public static , I> SerializableContextual create(String contextId, C contextual, ContextualStore contextualStore,
            BeanIdentifierIndex beanIdentifierIndex) {
        if (contextual instanceof Bean) {
            if (contextual instanceof PassivationCapable) {
                return new PassivationCapableSerializableBean(contextId, Reflections. cast(contextual), contextualStore, beanIdentifierIndex);
            } else {
                return new DefaultSerializableBean(contextId, Reflections. cast(contextual), contextualStore, beanIdentifierIndex);
            }
        } else {
            if (contextual instanceof PassivationCapable) {
                return new PassivationCapableSerializableContextual(contextId, contextual, contextualStore, beanIdentifierIndex);
            } else {
                return new DefaultSerializableContextual(contextId, contextual, contextualStore, beanIdentifierIndex);
            }
        }
    }

    private static final class SerializableContextualHolder, I> implements Serializable {

        private static final long serialVersionUID = 46941665668478370L;

        @SuppressWarnings(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 not be built yet
                Integer idx = beanIdentifierIndex.isBuilt() ? beanIdentifierIndex.getIndex(beanIdentifier) : null;
                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 contextualStore.putIfAbsent(contextual);
        }

        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(). getContextual(getBeanIdentifierIndex().getIdentifier(identifierIndex));
            } else if (identifier != null) {
                cached = getContextualStore(). getContextual(identifier);
            }
            if (cached == null) {
                throw new IllegalStateException("Error restoring serialized contextual with id " + identifier);
            }
        }

    }

    private abstract static class AbstractSerializableBean, I> extends ForwardingBean implements SerializableContextual, WrappedContextual {

        private static final long serialVersionUID = 7594992948498685840L;

        private final SerializableContextualHolder holder;

        AbstractSerializableBean(String contextId, B bean, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) {
            this.holder = new SerializableContextualHolder(contextId, bean, contextualStore, beanIdentifierIndex);
        }

        @Override
        public B get() {
            return holder.get();
        }

        @Override
        public Bean 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, I> extends ForwardingContextual implements
            SerializableContextual, WrappedContextual {

        private static final long serialVersionUID = 107855630671709443L;

        private final SerializableContextualHolder holder;

        AbstractSerializableContextual(String contextId, C contextual, ContextualStore contextualStore, BeanIdentifierIndex beanIdentifierIndex) {
            this.holder = new SerializableContextualHolder(contextId, contextual, contextualStore, beanIdentifierIndex);
        }

        @Override
        public Contextual 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, I> extends AbstractSerializableContextual {

        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 & PassivationCapable, I> extends AbstractSerializableContextual
            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, I> extends AbstractSerializableBean {

        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 & PassivationCapable, I> extends AbstractSerializableBean 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();
        }
    }
}