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     *****************************************************************************/
009    package org.picocontainer.behaviors;
010    
011    import org.picocontainer.ComponentAdapter;
012    import org.picocontainer.LifecycleStrategy;
013    import org.picocontainer.ObjectReference;
014    import org.picocontainer.PicoCompositionException;
015    import org.picocontainer.PicoContainer;
016    import org.picocontainer.ComponentLifecycle;
017    
018    import java.lang.reflect.Type;
019    import 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     * @param <T>
027     */
028    @SuppressWarnings("serial")
029    public class Stored<T> extends AbstractBehavior<T> {
030    
031        private final ObjectReference<Instance<T>> instanceReference;
032        private final ComponentLifecycle lifecycleDelegate;
033    
034        public Stored(ComponentAdapter<T> delegate, ObjectReference<Instance<T>> reference) {
035            super(delegate);
036            instanceReference = reference;
037            this.lifecycleDelegate = hasLifecycle(delegate)
038                    ? new RealComponentLifecycle<T>() : new NoComponentLifecycle<T>();
039        }
040    
041        private void guardInstRef() {
042            if (instanceReference.get() == null) {
043                instanceReference.set(new Instance());
044            }
045        }
046    
047        public boolean componentHasLifecycle() {
048            return lifecycleDelegate.componentHasLifecycle();
049        }
050    
051        /**
052         * Disposes the cached component instance
053         * {@inheritDoc}
054         */
055        public void dispose(PicoContainer container) {
056            lifecycleDelegate.dispose(container);
057        }
058    
059        /**
060         * Retrieves the stored reference.  May be null if it has
061         * never been set, or possibly if the reference has been
062         * flushed.
063         *
064         * @return the stored object or null.
065         */
066        public T getStoredObject() {
067            guardInstRef();
068            return instanceReference.get().instance;
069        }
070    
071        /**
072         * Flushes the cache.
073         * If the component instance is started is will stop and dispose it before
074         * flushing the cache.
075         */
076        public void flush() {
077            Instance<T> inst = instanceReference.get();
078            if (inst != null) {
079                Object instance = inst.instance;
080                if (instance != null && instanceReference.get().started) {
081                    stop(instance);
082                    dispose(instance);
083                }
084                instanceReference.set(null);
085            }
086        }
087    
088        public T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
089            guardInstRef();
090            T instance = instanceReference.get().instance;
091            if (instance == null) {
092                instance = super.getComponentInstance(container, into);
093                instanceReference.get().instance = instance;
094            }
095            return instance;
096        }
097    
098        public String getDescriptor() {
099            return "Stored" + getLifecycleDescriptor();
100        }
101    
102        protected String getLifecycleDescriptor() {
103            return (lifecycleDelegate.componentHasLifecycle() ? "+Lifecycle" : "");
104        }
105    
106        /**
107         * Starts the cached component instance
108         * {@inheritDoc}
109         */
110        public void start(PicoContainer container) {
111            lifecycleDelegate.start(container);
112        }
113    
114        /**
115         * Stops the cached component instance
116         * {@inheritDoc}
117         */
118        public void stop(PicoContainer container) {
119            lifecycleDelegate.stop(container);
120    
121        }
122    
123        public boolean isStarted() {
124            return lifecycleDelegate.isStarted();
125        }
126    
127        private class RealComponentLifecycle<T> implements ComponentLifecycle<T>, Serializable {
128    
129            public void start(PicoContainer container) {
130                guardInstRef();
131                guardAlreadyDisposed();
132                guardStartState(true, "already started");
133                // Lazily make the component if applicable
134                Stored.this.start(getComponentInstance(container, NOTHING.class));
135                instanceReference.get().started = true;
136            }
137    
138            public void stop(PicoContainer container) {
139                guardInstRef();
140                guardAlreadyDisposed();
141                guardNotInstantiated();
142                guardStartState(false, "not started");
143                Stored.this.stop(instanceReference.get().instance);
144                instanceReference.get().started = false;
145    
146            }
147    
148            public void dispose(PicoContainer container) {
149                guardInstRef();
150                guardNotInstantiated();
151                guardAlreadyDisposed();
152                Stored.this.dispose(instanceReference.get().instance);
153                instanceReference.get().disposed = true;
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    
213    
214    }