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     */
027    @SuppressWarnings("serial")
028    public 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    }