001/*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.            *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD      *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file.                                                     *
007 *                                                                           *
008 *****************************************************************************/
009package org.picocontainer.behaviors;
010
011import org.picocontainer.ComponentAdapter;
012import org.picocontainer.LifecycleStrategy;
013import org.picocontainer.ObjectReference;
014import org.picocontainer.PicoCompositionException;
015import org.picocontainer.PicoContainer;
016import org.picocontainer.ComponentLifecycle;
017
018import java.lang.reflect.Type;
019import java.io.Serializable;
020
021/*
022 * behaviour for all behaviours wishing to store
023 * their component in "awkward places" ( object references )
024 *
025 * @author Konstantin Pribluda
026 */
027@SuppressWarnings("serial")
028public class Stored<T> extends AbstractBehavior<T> {
029
030    private final ObjectReference<Instance<T>> instanceReference;
031    private final ComponentLifecycle lifecycleDelegate;
032
033    public Stored(ComponentAdapter<T> delegate, ObjectReference<Instance<T>> reference) {
034        super(delegate);
035        instanceReference = reference;
036        this.lifecycleDelegate = hasLifecycle(delegate)
037                ? new RealComponentLifecycle<T>() : new NoComponentLifecycle<T>();
038    }
039
040    private void guardInstRef() {
041        if (instanceReference.get() == null) {
042            instanceReference.set(new Instance<T>());
043        }
044    }
045
046    public boolean componentHasLifecycle() {
047        return lifecycleDelegate.componentHasLifecycle();
048    }
049
050    /**
051     * Disposes the cached component instance
052     * {@inheritDoc}
053     */
054    public void dispose(PicoContainer container) {
055        lifecycleDelegate.dispose(container);
056    }
057
058    /**
059     * Retrieves the stored reference.  May be null if it has
060     * never been set, or possibly if the reference has been
061     * flushed.
062     *
063     * @return the stored object or null.
064     */
065    public T getStoredObject() {
066        guardInstRef();
067        return instanceReference.get().instance;
068    }
069
070    /**
071     * Flushes the cache.
072     * If the component instance is started is will stop and dispose it before
073     * flushing the cache.
074     */
075    public void flush() {
076        Instance<T> inst = instanceReference.get();
077        if (inst != null) {
078            Object instance = inst.instance;
079            if (instance != null && instanceReference.get().started) {
080                stop(instance);
081                dispose(instance);
082            }
083            instanceReference.set(null);
084        }
085    }
086
087    public T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
088        guardInstRef();
089        T instance = instanceReference.get().instance;
090        if (instance == null) {
091            instance = super.getComponentInstance(container, into);
092            instanceReference.get().instance = instance;
093        }
094        return instance;
095    }
096
097    public String getDescriptor() {
098        return "Stored" + getLifecycleDescriptor();
099    }
100
101    protected String getLifecycleDescriptor() {
102        return (lifecycleDelegate.componentHasLifecycle() ? "+Lifecycle" : "");
103    }
104
105    /**
106     * Starts the cached component instance
107     * {@inheritDoc}
108     */
109    public void start(PicoContainer container) {
110        lifecycleDelegate.start(container);
111    }
112
113    /**
114     * Stops the cached component instance
115     * {@inheritDoc}
116     */
117    public void stop(PicoContainer container) {
118        lifecycleDelegate.stop(container);
119    }
120
121    public boolean isStarted() {
122        return lifecycleDelegate.isStarted();
123    }
124
125    private class RealComponentLifecycle<T> implements ComponentLifecycle<T>, Serializable {
126
127        public void start(PicoContainer container) {
128            guardInstRef();
129            guardAlreadyDisposed();
130            guardStartState(true, "already started");
131            // Lazily make the component if applicable
132            Stored.this.start(getComponentInstance(container, NOTHING.class));
133            instanceReference.get().started = true;
134        }
135
136        public void stop(PicoContainer container) {
137            guardInstRef();
138            guardAlreadyDisposed();
139            guardNotInstantiated();
140            guardStartState(false, "not started");
141            Stored.this.stop(instanceReference.get().instance);
142            instanceReference.get().started = false;
143
144        }
145
146        public void dispose(PicoContainer container) {
147            guardInstRef();
148            Instance<?> instance = instanceReference.get();
149            if (instance.instance != null) {
150                guardAlreadyDisposed();
151                Stored.this.dispose(instance.instance);
152                instance.disposed = true;
153            }
154        }
155
156
157        private void guardNotInstantiated() {
158            if (instanceReference.get().instance == null)
159                throw new IllegalStateException("'" + getComponentKey() + "' not instantiated");
160        }
161
162        private void guardStartState(boolean unexpectedStartState, String message) {
163            if (instanceReference.get().started == unexpectedStartState)
164                throw new IllegalStateException("'" + getComponentKey() + "' " + message);
165        }
166
167        private void guardAlreadyDisposed() {
168            if (instanceReference.get().disposed)
169                throw new IllegalStateException("'" + getComponentKey() + "' already disposed");
170        }
171
172        public boolean componentHasLifecycle() {
173            return true;
174        }
175
176        public boolean isStarted() {
177            guardInstRef();
178            return instanceReference.get().started;
179        }
180    }
181
182    private static class NoComponentLifecycle<T> implements ComponentLifecycle<T>, Serializable {
183        public void start(PicoContainer container) {
184        }
185
186        public void stop(PicoContainer container) {
187        }
188
189        public void dispose(PicoContainer container) {
190        }
191
192        public boolean componentHasLifecycle() {
193            return false;
194        }
195
196        public boolean isStarted() {
197            return false;
198        }
199    }
200
201    private static boolean hasLifecycle(ComponentAdapter delegate) {
202        return delegate instanceof LifecycleStrategy
203                && ((LifecycleStrategy) delegate).hasLifecycle(delegate.getComponentImplementation());
204    }
205
206    public static class Instance<T> implements Serializable {
207        private T instance;
208        protected boolean started;
209        protected boolean disposed;
210    }
211
212}